繁体   English   中英

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

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

C ++规范说默认析构函数删除所有非静态成员。 然而,我无法实现这一目标。

我有这个:

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;
};

然后这应该打印给定的消息,但它不会:

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

是什么让你认为对象n指向的应该默认删除? 默认的析构函数会破坏指针,而不是它指向的指针。

编辑:我会看看能不能说清楚一点。

如果你有一个本地指针,并且它超出范围,你会期望它指向的对象被销毁吗?

{
    Thing* t = new Thing;

    // do some stuff here

    // no "delete t;"
}

t指针被清理,但它指向的Thing不是。 这是一个泄漏。 基本上同样的事情发生在你的班上。

想象一下这样的事情:

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

你可以自己使用C ++中的指针。 没有人会自动删除它们。

默认的析构函数确实会销毁所有成员对象。 但是在这种情况下,成员对象本身就是一个指针,而不是指向它的东西。 这可能会让你困惑。

但是,如果不使用简单的内置指针,则会使用智能指针 ,破坏这样的“指针”(实际上是一个类)可能会触发指向对象的破坏。

默认的析构函数正在销毁指针。 如果要使用M的默认析构函数删除N ,请使用智能指针。 改变N * n; to auto_ptr<N> n; n将被销毁。

编辑:正如评论中所指出的, auto_ptr<>并不是所有用途的最佳智能指针,但它看起来像这里所要求的。 它具体代表所有权:M中的N在M的持续时间内不再存在。 复制或分配auto_ptr<>表示所有权的更改,这通常不是您想要的。 如果你想从M传递一个指针,你应该从n.get()传递一个N * n.get()

更通用的解决方案是boost::shared_ptr<> ,它将采用C ++ 0x标准。 在任何使用原始指针的地方都可以使用它。 它不是最有效的构造,并且在循环引用方面存在问题,但它通常是一种安全的构造。

另一个编辑:要在另一个注释中回答问题,默认析构函数的标准行为是销毁所有数据成员和基类。 但是,删除原始指针只会删除指针,而不是它指向的指针。 毕竟,实现无法知道这是唯一的指针,还是重要的指针,或类似的东西。 智能指针背后的想法是删除智能指针至少会导致删除它指向的内容,这通常是所需的行为。

当指向对象似乎属于包含的对象时,是否有任何理由使用指针? 只需按值存储对象:

class M
{
    N n;

public:

    M() : n()
    {
    }
};

说析构函数删除成员是不正确的。 它调用每个成员(和基类)的析构函数,对于内置类型(如指针),它意味着什么都不做。

s与delete s匹配是您的责任(手动或智能指针的帮助)。

你的论点看似合理,但这并不是指针的工作原理。

n实际上是被破坏但是,这意味着N*析构函数被调用,它不会破坏n指向的任何对象。 N*的析构函数想象成int的析构函数。 它删除了它的值,对于指针也是如此,它会删除它指向的地址,但它不需要删除任何位于你刚删除的地址的对象。

我想你可能会对这里的间接水平感到困惑。 当一个实例被销毁时,每个数据成员确实会随之被销毁。 在你的情况下,当一个M被销毁并调用M::~M() ,它的变量n真的被破坏了。 问题是nN * ,所以当指针被销​​毁时,指向的东西不是。

delete不起作用。 考虑你的简单陈述:

delete n;

上面的语句破坏了n指向的东西,它是N类型的对象。 它不会破坏n本身,这是一个N *指针。

有一个很好的理由, M::~M()不会自动调用delete n; 这就是:所引用的N对象可能在几个M对象之间共享,如果一个M被破坏,其余的就会丢失他们指向的N ,到处留下可怕的悬空指针。 C ++并不试图解释你的指针什么意思 ,它只是做你告诉它做的事情。

简而言之,当它被摧毁时, M真的正在摧毁它的所有成员,只是这种破坏不会做你认为它应该做的事情。 如果你想要一个获取对象所有权并在销毁指针时销毁它的指针类型,请查看std::auto_ptr

默认的析构函数如下所示:

~M()
{
}

默认的析构函数不会插入代码来执行任何有针对性的事情。 如果你有n指向堆栈变量怎么办? 自动插入删除n会崩溃。

默认的析构函数在类的每个成员( 成员.~T() )上调用析构函数。 对于一个指针,这是一个无操作(什么都不做),就像myint一样.~int()什么都不做,但是对于定义了析构函数的成员类,会调用析构函数。

这是另一个例子:

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

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

实际上默认的析构函数是这样做的:

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
}

当然,如果你定义了一个析构函数,你的析构函数中的代码会在成员变量被销毁之前运行(显然,否则它们将无效!)。

尽量避免使用指针。 它们是最后的手段。

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
};

然后以这种方式使用它

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

这将显示N类型的销毁对象

我想你可以从一个非常简单的例子中受益:

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

这也不会打印任何东西。

为什么? 因为pointern )被破坏,而不是指向*n的对象。

当然,你不希望它破坏指向的对象:

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

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

您应该记住,与C#Java等语言不同,有两种方法可以在C ++中创建对象:直接N myObject (在堆栈上)或通过newnew N()中,在这种情况下,对象放在堆上,你有责任在以后释放它。

所以你的析构函数会破坏指针,但不会破坏指向的对象。 在没有新对象的情况下分配对象(并且不使用指针),或者如果您希望它是自动的,请使用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;
};

现在的期望是:

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

只有当类M来自N时才会发生:

class M: Class N {
...

只有在这种情况下,

M* m = new M()

将调用N的构造函数,然后调用M的构造函数,其中as

delete m;

将首先自动调用M的析构函数然后调用N.

暂无
暂无

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

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