C ++以及何时使用删除 [英] C++ and when to use delete

查看:63
本文介绍了C ++以及何时使用删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在重新阅读不久前在C ++上的一些代码(我现在正在学校里学习Java), 我对于何时必须使用delete感到有些困惑.

例如: 声明两个对象时:

Fraction* f1;
Fraction* f2;

并像这样创建f1和f2:

f1 = new Fraction(user_input1, user_input2);
f2 = new Fraction(user_input3, user_input4);

下次我要使用new运算符创建新对象时,是否必须先删除?我很困惑,因为我习惯于用Java中的垃圾收集器来处理对象及其删除.我必须先删除才能再次使用new吗?

if (f1) delete f1;

if (f2) delete f2;

//initialize again...

解决方案

而不是告诉您何时使用delete,而是尝试解释为什么仍然使用指针.因此,您可以决定何时使用动态对象,如何使用它们以及何时调用delete(而不是).


规则的拇指:

  • 尽可能使用静态对象,然后在需要时创建指向该实例的指针.无需delete通话.
  • 如果创建指向动态对象的指针,请创建清理代码.因此,当您写new时,还要在适当的位置写delete(并确保已被调用).
  • 对于每个new关键字,都需要 作为delete关键字.否则,您将占用计算机已导致应用程序崩溃或停止的所有资源.也会使系统变慢.

对象的静态创建:

Fraction f1;

  • 无需删除任何内容,退出退出时将对其进行处理.

动态创建对象:

Fraction* f1;

现在,您具有该地址到堆上的内存块.这是无效的,因为您还没有分配任何东西.好的做法是-根据声明的位置-为它分配一个NULL(Windows)或0(跨平台).

Fraction* f1 = 0;


何时使用delete

创建动态对象并调用new运算符后,就需要在某个位置调用delete.

int main()
{

    Fraction* f1 = 0;    // Good practise to avoid invalid pointers
                         // An invalid pointer - if( f1 ){ Access violation }

    f1 = new Fraction(); // Could have done this at the previous line

    /* do whatever you need */

    if( f1 )
    {

        delete f1; 
        f1 = 0;          // not needed since we are leaving the application

    }

    return 0;

}

在某些情况下,使用分数数组或指向它的指针可能会很有用.为了简化起见,此处使用int,与跳过错误处理相同:

int arr[ 10 ];
int cur = -1;
int* Add( int fraction )
{
    arr[++cur] = fraction;
    return &arr[cur];
}

// Usage:
Add( 1 );
Add( 4 );

这里发生的一件事情,没有通过动态对象分配任何内存.它们会自动释放.该函数返回的指针是指向静态内存块的指针.

将arr用作指向int的指针时:

int* arr[ 10 ];
int cur = -1;
int* Add( int* fraction )
{
    arr[++cur] = fraction;
    return arr[cur];
}

// Usage:
int* test;

test = Add( new int( 1 ) );
test = Add( new int( 4 ) );

现在,由于没有清理代码,您必须要泄漏的内存块.

在每个Add(...)delete test之后调用时,您已经清理了内存,但是丢失了存储在int* arr[ 10 ]中的值,因为它们指向保存该值的内存.

您可以创建另一个函数,并在完成这些值后调用此函数:

void CleanUp()
{
    for( int a = 0; a < 10; ++a )
        delete arr[ a ];
}

小用法示例:

int* test;
int  test2;

test  = Add( new int( 1 ) );
test2 = *Add( new int( 4 ) ); // dereference the returned value

/* do whatever you need */

CleanUp();


我们为什么要使用指针:

int Add( int val )
{
    return val; // indeed very lame
}

当调用需要参数(类型)的函数时,不是传递实例,而是传递它的副本.在上面的函数中,您将返回该副本的副本.它将涉及所有内存的大量重复工作,使您的应用程序运行速度大大降低.

考虑一下:

class Test
{
    int  t;
    char str[ 256 ];
}

如果一个函数需要类型Test,那么您正在复制int和256个字符.因此,使该函数只需要一个指向Test的指针即可.然后使用指针指向的内存,不需要复制.

int Add( int val )
{
    val++;
    return val;
}

在最后一个示例中,我们将1添加到val的副本中,然后返回该副本.

int i = Add( 1 );

结果: i = 2;

void Add( int* val )
{
    // mind the return type
    *val++;
}

在此示例中,您将地址传递给一个值,然后-在取消引用后-将一个添加到该值.

int i = 1;
Add( &i );

结果: i = 2;

现在,您已将地址传递到i,而不是复制它.在函数中,您可以直接在该存储块的值上加1.因为更改了内存本身,所以您什么也不会返回.


清除/测试有效指针

有时您会遇到诸如以下的示例:

if( p != 0 ) // or if( p )
{
    /* do something with p */
}

这只是检查指针p是否有效.但是,无效地址(也就是不指向您已保留的内存(访问冲突))也将通过.对于您的代码,无效的指针是有效的地址.

因此,要使用此类检查,您必须将指针NULL(或0).

Fraction* f1 = 0;

当f1 == 0时,它不会指向任何东西,否则会指向它所指向的任何东西.

当您在已创建或未创建的主"类中有一个指针时,这很有用.

class Fraction
{
    public:
    int* basicFeature;
    int* ExtendedFeature = 0; // NULL this pointer since we don't know if it
                              // will be used
    Fraction( int fraction )
    {
        // Create a pointer owned by this class
        basicFeature = new int( fraction );
    }
    Fraction( int fraction, int extended ) // mind the static
    : Fraction( fraction )
    {
        // Create a pointer owned by this class
        ExtendedFeature = new int( extended );
    }
    ~Fraction()
    {
        delete basicFeature;
        if( ExtendedFeature )
            // It is assigned, so delete it
            delete ExtendedFeature;
    }
}

使用ctor创建两个指针,因此在dtor中我们清理了这些指针.仅检查ExtendedFeature,因为可能会创建也可能不会创建.始终创建basicFeature.

您可以通过调用新函数removeExtendedFeature()来替换if语句及其在dtor中的范围:removeExtendedFeature()该函数的实现将是:

Fraction::removeExtendedFeature()
{
    if( ExtendedFeature )
    {
        // It is assigned, so delete it
        delete ExtendedFeature;
        // Now it is important to NULL the pointer again since you would
        // get an access violation on the clean up of the instance of 
        // the class Fraction
        ExtendedFeature = 0;
    }
}

还有新的dtor:

Fraction::~Fraction()
{
    delete basicFeature;
    removeExtendedFeature();
}

另一个清零功能可能是:

int Fraction::getValue()
{
    int result = *basicFeature;
    if( ExtendedFeature )
        result += *ExtendedFeature;
    return result;
}


我对the脚类Fraction(具有更la脚的扩展功能)表示歉意.但举个例子,它可以达到目的.

I am re-reading some code from a while ago on C++ (I am learning Java in school right now), and I am a little confused as to when I must use delete.

For example: When declaring two objects:

Fraction* f1;
Fraction* f2;

And create f1 and f2 like this:

f1 = new Fraction(user_input1, user_input2);
f2 = new Fraction(user_input3, user_input4);

The next time I want to use new operator to create a new object, do I have to delete first? I am confused because I am used to having the garbage collector in java take care of objects and their deletion. Do I have to delete before using new again?

if (f1) delete f1;

if (f2) delete f2;

//initialize again...

解决方案

Rather then telling you when to use delete, I'll try to explain why you use pointers anyway. So you can decide when to use dynamic objects, how to use them and so when to call delete (and not).


Thumb of rules:

  • Use static objects where possible, then when needed create a pointer to that instance. No delete call needed.
  • If you create a pointer to a dynamic object, create clean up code. So when you write new also write delete somehwere at a suitable location (and make sure that is called).
  • For every new keyword there needs to be a delete keyword. Otherwise you are taking all the resources the machine has resulting in applications to crash or just stop. Also it will make the system slower.

Static creation of an object:

Fraction f1;

  • No need to delete anything, that is handled when exiting the scoop it is created in.

Dynamic creation of an object:

Fraction* f1;

Now you have this address to a memory block on the heap. It is an invalid one since you haven't assigned anything to it. Good practise would be - depending on where you declare it - to assign it a NULL (windows) or 0 (cross-platform).

Fraction* f1 = 0;


When to use delete

As soon as you create a dynamic object, thus calling the new operator, you need to call delete somewhere.

int main()
{

    Fraction* f1 = 0;    // Good practise to avoid invalid pointers
                         // An invalid pointer - if( f1 ){ Access violation }

    f1 = new Fraction(); // Could have done this at the previous line

    /* do whatever you need */

    if( f1 )
    {

        delete f1; 
        f1 = 0;          // not needed since we are leaving the application

    }

    return 0;

}

In some scenarios it could be usefull to have an array of Fraction, or pointers to it. Using an int for simplicity here, same as skipping error handling:

int arr[ 10 ];
int cur = -1;
int* Add( int fraction )
{
    arr[++cur] = fraction;
    return &arr[cur];
}

// Usage:
Add( 1 );
Add( 4 );

One thing happening here, no assignment to any memory through dynamic objects. They are freed automatically. The pointer returned by the function is a pointer to a static memory block.

When making the arr as pointers to int:

int* arr[ 10 ];
int cur = -1;
int* Add( int* fraction )
{
    arr[++cur] = fraction;
    return arr[cur];
}

// Usage:
int* test;

test = Add( new int( 1 ) );
test = Add( new int( 4 ) );

Now you have to memory blocks which are leaking since you have no clean up code.

When you call after each Add(...) the delete test, you have cleaned up the memory but you have lost the values you had stored within int* arr[ 10 ] as they are pointing to the memory holding the value.

You can create another function and call this after you are done with those values:

void CleanUp()
{
    for( int a = 0; a < 10; ++a )
        delete arr[ a ];
}

Small usage example:

int* test;
int  test2;

test  = Add( new int( 1 ) );
test2 = *Add( new int( 4 ) ); // dereference the returned value

/* do whatever you need */

CleanUp();


Why do we want to use pointers:

int Add( int val )
{
    return val; // indeed very lame
}

When you call a function that needs a parameter (type), you are not passing in the instance but rather a copy of it. In the above function you are returning a copy of that copy. It will amount into a lot of duplication all memory involved and you make your application tremendously much slower.

Consider this:

class Test
{
    int  t;
    char str[ 256 ];
}

If a function needs a type Test, you are copying the int and 256 chars. So make the function so it needs only a pointer to Test. Then the memory the pointer is pointing to is used and no copying is needed.

int Add( int val )
{
    val++;
    return val;
}

Within this last example, we are adding 1 to the copy of val and then returning a copy of that.

int i = Add( 1 );

result: i = 2;

void Add( int* val )
{
    // mind the return type
    *val++;
}

In this example you are passing the address to a value and then - after dereferencing - adding one to the value.

int i = 1;
Add( &i );

result: i = 2;

Now you have passed in the address to i, not making a copy of it. Within the function you directly adding 1 to the value at that memory block. You return nothing since you have altered the memory itself.


Nulling/testing for valid pointers

Sometime you encounter examples such as:

if( p != 0 ) // or if( p )
{
    /* do something with p */
}

This is just to check if the pointer p is valid. However, an invalid address - thus not pointing to a memory you have reserved (the access violation) - will pass through too. For your code, an invalid pointer is a valid address.

Therefore, to use such a check you have to NULL (or 0) the pointer.

Fraction* f1 = 0;

When f1 == 0, it doesn't point to anything otherwise it points to whatever it points to.

This is usefull when you have a pointer in a 'main'-class which is or isn't created.

class Fraction
{
    public:
    int* basicFeature;
    int* ExtendedFeature = 0; // NULL this pointer since we don't know if it
                              // will be used
    Fraction( int fraction )
    {
        // Create a pointer owned by this class
        basicFeature = new int( fraction );
    }
    Fraction( int fraction, int extended ) // mind the static
    : Fraction( fraction )
    {
        // Create a pointer owned by this class
        ExtendedFeature = new int( extended );
    }
    ~Fraction()
    {
        delete basicFeature;
        if( ExtendedFeature )
            // It is assigned, so delete it
            delete ExtendedFeature;
    }
}

withing the ctors we are creating the two pointers, so within the dtor we are cleaning up those pointer. Only checking the ExtendedFeature since this one may or may not be created. basicFeature is always created.

You could replace the if statement including its scope within the dtor by calling a new function: removeExtendedFeature() where that function implementation would be:

Fraction::removeExtendedFeature()
{
    if( ExtendedFeature )
    {
        // It is assigned, so delete it
        delete ExtendedFeature;
        // Now it is important to NULL the pointer again since you would
        // get an access violation on the clean up of the instance of 
        // the class Fraction
        ExtendedFeature = 0;
    }
}

And the new dtor:

Fraction::~Fraction()
{
    delete basicFeature;
    removeExtendedFeature();
}

Another functionallity of nulling could be:

int Fraction::getValue()
{
    int result = *basicFeature;
    if( ExtendedFeature )
        result += *ExtendedFeature;
    return result;
}


My apologies for the lame class Fraction, with an ever more lame extended feature. But as an example it would serve the purpose.

这篇关于C ++以及何时使用删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆