[英]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
真的被破坏了。 问题是n
是N *
,所以当指针被销毁时,指向的东西不是。
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
这也不会打印任何东西。
为什么? 因为pointer
( n
)被破坏,而不是指向*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
(在堆栈上)或通过new
在new 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.