请考虑以下代码:
#include <iostream>
struct A
{
A(){ };
~A(){ std::cout << "~A::A()" << std::endl; };
};
struct B: A { };
B *b = new B; //Doesn't produce any side-effect.
int main(){ }
程序不会产生任何输出,这意味着不会调用析构函数。但是如果我们用delete
说明符替换析构函数的主体,程序甚至都不会编译。
#include <iostream>
struct A
{
A(){ };
~A() = delete; //{ std::cout << "~A::A()" << std::endl; };
};
struct B: A { };
B *b = new B; //Error: use of deleted function
int main(){ }
由于调用了删除的功能。那是在这种情况下被调用的析构函数。为什么会有这样的差异?
即使我们明确定义B
的构造函数,它也不会起作用:
#include <iostream>
struct A
{
A(){ };
~A() = delete; //{ std::cout << "~A::A()" << std::endl; };
};
struct B: A
{
B(){ };
};
B *b = new B;
int main(){ }
答案 0 :(得分:16)
问题是编译器删除了B
的构造函数,否则默认定义将是错误的。这是因为A
没有析构函数,如果B
无法销毁,B
的默认构造函数无法从A
构造A
。如果用g ++编译,那就是你得到的错误:
note: 'B::B()' is implicitly deleted because the default definition would be ill-formed:
或clang ++:
error: call to implicitly-deleted default constructor of 'B'
如果您明确声明构造函数B(){}
,那么它会因为删除A
而无法销毁B
A::~A()
部分而抱怨。
在编译时检查B
隐藏其父A
的能力,因此您收到错误。
+1的问题。看起来你不能继承(然后使用一个实例)来自具有已删除的析构函数的类,尽管这是我第一次碰到这个问题。
答案 1 :(得分:15)
标准的适用部分是(引用N4140):
§12.4[class.dtor] / p11:
如果调用析构函数或指定了析构函数,则可能会被调用 在5.3.4和12.6.2中。如果是析构函数,程序就会形成错误 可能被调用的内容被删除或无法从上下文访问 调用。
§12.6.2[class.base.init] / p10:
在非委托构造函数中,每个潜在的析构函数 可能会调用构造的类类型的子对象(12.4)。 [ 注意:此规定确保在抛出异常时可以为完全构造的子对象调用析构函数(15.2)。 - 结束记录]
§12[特别] / p5:
对于一个类,它的非静态数据成员,它的非虚拟直接基础 类,如果类不是抽象的(10.4),它的虚拟基础 类被称为可能构建的子对象。
由于A
是B
的非虚拟直接基础,因此是可能构建的B
子对象,因此其析构函数可能被调用<{em>}在B::B()
中,并且由于该析构函数被删除,因此该程序格式不正确。
答案 2 :(得分:11)
关于我认为你的基本问题:为什么你不能建构
一个B
,即使它只是A
的析构函数
exists:在B
的构造函数中,编译器自动生成
如果存在异常,则调用A
的析构函数的代码。如果是A
要用作基类,必须具有可访问的析构函数
(公共的或受保护的)。
在你的情况下,当然,B
的构造函数不能抛出,等等
A::~A()
永远不会被调用。但编译器不能总是
确定是否是这种情况,标准不要求
它甚至尝试。编译器必须假设B::B()
的主体可以
抛出(在完全构造A
之后)。即使它可以
确定B::B()
不能抛出,并且不生成代码
调用A
的析构函数,这是一个优化,但不是
允许改变代码的合法性。
答案 3 :(得分:5)
基于第一个代码:
#include <iostream>
struct A
{
A(){ };
~A(){ std::cout << "~A::A()" << std::endl; };
};
struct B: A { };
B *b = new B; //Doesn't produce any side-effect.
int main(){ }
尝试以下方法:
#include <iostream>
struct A
{
A(){ };
~A(){ std::cout << "~A::A()" << std::endl; };
};
struct B: A { };
int main(){
B b;
}
答案 4 :(得分:2)
C ++ 14对此很清楚:
[C++14: 12.6.2/10]:
在非委托构造函数中,可能会调用每个可能构造的类类型子对象的析构函数(12.4)。 [注意:此规定确保在抛出异常时可以为完全构造的子对象调用析构函数(15.2)。 -end note]
C ++ 11中没有it was added in issue 1424这样的措辞,但由于这个事实不容忽视,所以在C ++ 11和C ++ 98/03实现中也是如此。
所以,虽然你仍然没有调用析构函数,但继承的存在意味着B()
需要~A()
可以调用:基本上,这意味着< em>可访问和未删除。
答案 5 :(得分:0)
回答你的标题问题:如果你不调用delete
那么就不会发生删除,尽管对象使用的内存可以通过终止创建它的程序来释放,这可能会导致要做的几件事之一:
嗯,除了最后一个,你得到了要点,最后一个只是一个喜欢多愁善感加强程序员自我:D
对此唯一的限制因素是孤立对象(其中指向内存位置的指针在代码中的某个点丢失,see here for example)和具有已删除析构函数的对象(see here for a brief explanation):这些不能删除因为在第一个实例中它们不再是可寻址的,而在第二个实例中它们是一个错误的*对象,因为它们没有析构函数(*错误,因为不符合标准用途/规范,但有时候当您可能想要在拥有进程终止之前/在某个特定情况singleton之前阻止某个对象被自己删除时,但是,良好的编程/逻辑应该可以防止对析构函数完全不可用的需要)。
如果您需要更多信息,或者上述任何需要澄清,请告诉我,我将非常乐意为您提供帮助:)