简体   繁体   English

为什么C ++默认析构函数不会破坏我的对象?

[英]Why doesn't the C++ default destructor destroy my objects?

The C++ specification says the default destructor deletes all non-static members. C ++规范说默认析构函数删除所有非静态成员。 Nevertheless, I can't manage to achieve that. 然而,我无法实现这一目标。

I have this: 我有这个:

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
        n = new N;
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N* n;
};

Then this should print the given message, but it doesn't: 然后这应该打印给定的消息,但它不会:

M* m = new M();
delete m; //this should invoke the default destructor

What makes you think the object n points to should be deleted by default? 是什么让你认为对象n指向的应该默认删除? The default destructor destroys the pointer, not what it's pointing to. 默认的析构函数会破坏指针,而不是它指向的指针。

Edit: I'll see if I can make this a little more clear. 编辑:我会看看能不能说清楚一点。

If you had a local pointer, and it went out of scope, would you expect the object it points to to be destroyed? 如果你有一个本地指针,并且它超出范围,你会期望它指向的对象被销毁吗?

{
    Thing* t = new Thing;

    // do some stuff here

    // no "delete t;"
}

The t pointer is cleaned up, but the Thing it points to is not. t指针被清理,但它指向的Thing不是。 This is a leak. 这是一个泄漏。 Essentially the same thing is happening in your class. 基本上同样的事情发生在你的班上。

Imagine something like this: 想象一下这样的事情:

class M {
public:
    M() { }
//  ~M() {        // If this happens by default
//      delete n; // then this will delete an arbitrary pointer!
//  }
private:
    N* n;
};

You're on your own with pointers in C++. 你可以自己使用C ++中的指针。 No one will automatically delete them for you. 没有人会自动删除它们。

The default destructor will indeed destroy all member objects. 默认的析构函数确实会销毁所有成员对象。 But the member object in this case is a pointer itself, not the thing it points to. 但是在这种情况下,成员对象本身就是一个指针,而不是指向它的东西。 This might have confused you. 这可能会让你困惑。

However, if instead of a simple built-in pointer, you will use a smart pointer , the destruction of such a "pointer" (which is actually a class) might trigger the destruction of the object pointed to. 但是,如果不使用简单的内置指针,则会使用智能指针 ,破坏这样的“指针”(实际上是一个类)可能会触发指向对象的破坏。

The default destructor is destroying the pointer. 默认的析构函数正在销毁指针。 If you want to delete the N with M 's default destructor, use a smart pointer. 如果要使用M的默认析构函数删除N ,请使用智能指针。 Change N * n; 改变N * n; to auto_ptr<N> n; to auto_ptr<N> n; and n will be destroyed. n将被销毁。

Edit: As pointed out in comments, auto_ptr<> is not the best smart pointer for all uses, but it looks like what's called for here. 编辑:正如评论中所指出的, auto_ptr<>并不是所有用途的最佳智能指针,但它看起来像这里所要求的。 It specifically represents ownership: the N in an M is there for the duration of the M and no longer. 它具体代表所有权:M中的N在M的持续时间内不再存在。 Copying or assigning an auto_ptr<> represents a change in ownership, which is usually not what you want. 复制或分配auto_ptr<>表示所有权的更改,这通常不是您想要的。 If you wanted to pass a pointer from M, you should pass a N * gotten from n.get() . 如果你想从M传递一个指针,你应该从n.get()传递一个N * n.get()

A more general solution would be boost::shared_ptr<> , which will be in the C++0x standard. 更通用的解决方案是boost::shared_ptr<> ,它将采用C ++ 0x标准。 That can be used pretty much wherever a raw pointer would be used. 在任何使用原始指针的地方都可以使用它。 It's not the most efficient construct, and has problems with circular references, but it's generally a safe construct. 它不是最有效的构造,并且在循环引用方面存在问题,但它通常是一种安全的构造。

Another edit: To answer the question in another comment, the standard behavior of the default destructor is to destroy all data members and base classes. 另一个编辑:要在另一个注释中回答问题,默认析构函数的标准行为是销毁所有数据成员和基类。 However, deleting a raw pointer simply removes the pointer, not what it points to. 但是,删除原始指针只会删除指针,而不是它指向的指针。 After all, the implementation can't know if that's the only pointer, or the important one, or anything like that. 毕竟,实现无法知道这是唯一的指针,还是重要的指针,或类似的东西。 The idea behind smart pointers is that deleting a smart pointer will at least lead to the deletion of what it points to, which is usually the desired behavior. 智能指针背后的想法是删除智能指针至少会导致删除它指向的内容,这通常是所需的行为。

Is there any reason why you use a pointer when the pointed-to object seems to belong the contained object? 当指向对象似乎属于包含的对象时,是否有任何理由使用指针? Just store the object by value: 只需按值存储对象:

class M
{
    N n;

public:

    M() : n()
    {
    }
};

It is incorrect to say that the destructor deletes members. 说析构函数删除成员是不正确的。 It invokes the destructor of each member (and base class), which for built-in types (like pointers) means doing nothing. 它调用每个成员(和基类)的析构函数,对于内置类型(如指针),它意味着什么都不做。

Matching new s with delete s is your responsibility (either manually, or with the help of smart pointers). s与delete s匹配是您的责任(手动或智能指针的帮助)。

Your argument might seem sound but that's not how things work for pointers. 你的论点看似合理,但这并不是指针的工作原理。

n is actually being destructed but, what this means is that the N* destructor is being called which, it does NOT destruct whatever object n is pointing to. n实际上是被破坏但是,这意味着N*析构函数被调用,它不会破坏n指向的任何对象。 Think of the N* 's destructor as if it were an int 's destructor. N*的析构函数想象成int的析构函数。 It deletes its value, the same happens for a pointer, it deletes the address it is pointing to, but it doesn't need to delete whatever object is located at the address you just deleted. 它删除了它的值,对于指针也是如此,它会删除它指向的地址,但它不需要删除任何位于你刚删除的地址的对象。

I think you may be confused about levels of indirection here. 我想你可能会对这里的间接水平感到困惑。 When an instance is destroyed, each data member does indeed get destroyed along with it. 当一个实例被销毁时,每个数据成员确实会随之被销毁。 In your case, when an M is destroyed and M::~M() is called, its variable n really is destroyed. 在你的情况下,当一个M被销毁并调用M::~M() ,它的变量n真的被破坏了。 The problem is that n is a N * , so while the pointer is destroyed, the thing it points to is not. 问题是nN * ,所以当指针被销​​毁时,指向的东西不是。

delete does not work like this. delete不起作用。 Consider your simple statement: 考虑你的简单陈述:

delete n;

The above statement destroys the thing that n points to, which is an object of type N . 上面的语句破坏了n指向的东西,它是N类型的对象。 It does not destroy n itself, which is an N * pointer. 它不会破坏n本身,这是一个N *指针。

There is a very good reason that M::~M() does not automatically call delete n; 有一个很好的理由, M::~M()不会自动调用delete n; which is this: the N object referred to might be shared between several M objects, and if one M were destroyed, the rest would lose the N they were pointing at, leaving horrible dangling pointers everywhere. 这就是:所引用的N对象可能在几个M对象之间共享,如果一个M被破坏,其余的就会丢失他们指向的N ,到处留下可怕的悬空指针。 C++ does not attempt to interpret what you meant to do with your pointers, it just does what you told it to do. C ++并不试图解释你的指针什么意思 ,它只是做你告诉它做的事情。

In short, M really is destroying all of its members when it is destroyed, it's just that this destruction doesn't do what you think it should do. 简而言之,当它被摧毁时, M真的正在摧毁它的所有成员,只是这种破坏不会做你认为它应该做的事情。 If you want a pointer type which takes ownership of an object and destroys it when the pointer is destroyed, look at std::auto_ptr . 如果你想要一个获取对象所有权并在销毁指针时销毁它的指针类型,请查看std::auto_ptr

The default destructor looks like this: 默认的析构函数如下所示:

~M()
{
}

The default destructor does not insert code to do anything with pointed-to things. 默认的析构函数不会插入代码来执行任何有针对性的事情。 What if you had n pointing to a stack variable? 如果你有n指向堆栈变量怎么办? Automatically inserting a delete n would crash. 自动插入删除n会崩溃。

The default destructor calls the destructor on each member of the class ( member.~T() ). 默认的析构函数在类的每个成员( 成员.~T() )上调用析构函数。 For a pointer, that's a no-op (does nothing), just like myint.~int() does nothing, but for member classes with defined destructors, the destructor is called. 对于一个指针,这是一个无操作(什么都不做),就像myint一样.~int()什么都不做,但是对于定义了析构函数的成员类,会调用析构函数。

Here's another example: 这是另一个例子:

struct MyClass {
public:
    MyClass() { .. } // doesn't matter what this does

    int x;
    int* p;
    std::string s;
    std::vector<int> v;
};

The default destructor in reality is doing this: 实际上默认的析构函数是这样做的:

MyClass::~MyClass()
{
    // Call destructor on member variables in reverse order
    v.~std::vector<int>(); // frees memory
    s.~std::string();      // frees memory
    p.~int*();             // does nothing, no custom destructor
    x.~int();              // does nothing, no custom destructor
}

Of course, if you define a destructor, the code in your destructor runs before the member variables are destroyed (obviously, otherwise they would not be valid!). 当然,如果你定义了一个析构函数,你的析构函数中的代码会在成员变量被销毁之前运行(显然,否则它们将无效!)。

Try avoiding using pointers. 尽量避免使用指针。 They are last resort elements. 它们是最后的手段。

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
       // n = new N; no need, default constructor by default
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N n; // No pointer here
};

Then use it this way 然后以这种方式使用它

main(int, char**)
{
    M m;
}

This will display Destroying object of type N 这将显示N类型的销毁对象

I think you could benefit from a very simple example: 我想你可以从一个非常简单的例子中受益:

int main(int argc, char* argv[])
{
  N* n = new N();
} // n is destructed here

This will not print anything either. 这也不会打印任何东西。

Why ? 为什么? Because the pointer ( n ) is destructed, not the object pointed to *n . 因为pointern )被破坏,而不是指向*n的对象。

Of course, you would not want it to destroy the object pointed to: 当然,你不希望它破坏指向的对象:

int main(int argc, char* argv[])
{
  N myObject;
  {
    N* n = &myObject;
  } // n is destructed here, myObject is not

  myObject.foo();
} // myObject is destructed here

You should remember that unlike languages like C# or Java , there are 2 ways to create objects in C++: directly N myObject (on the stack) or via new like in new N() in which case the object is placed on the heap and YOU are reponsible for releasing it at a later time. 您应该记住,与C#Java等语言不同,有两种方法可以在C ++中创建对象:直接N myObject (在堆栈上)或通过newnew N()中,在这种情况下,对象放在堆上,你有责任在以后释放它。

So your destructor destroys the pointer, but not the object pointed to. 所以你的析构函数会破坏指针,但不会破坏指向的对象。 Allocate the object without new (and without using a pointer) or use a Smart Pointer if you want it to be automatic. 在没有新对象的情况下分配对象(并且不使用指针),或者如果您希望它是自动的,请使用Smart Pointer

M析构函数应该有'删除n'。

由于您使用new来创建实例,因此默认情况下不会删除它。

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
        n = new N;
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N* n;
};

and now the expectation is : 现在的期望是:

M* m = new M();
delete m; //this should invoke the default destructor

It will only happen, if the class M is derived from N: 只有当类M来自N时才会发生:

class M: Class N {
...

Only in this situation, 只有在这种情况下,

M* m = new M()

will call constructor of N and then constructor of M, where as 将调用N的构造函数,然后调用M的构造函数,其中as

delete m;

will automatically call destructor of M first and then N 将首先自动调用M的析构函数然后调用N.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM