在C ++中,如果我们将类析构函数定义为:
~Foo(){
return;
}
在调用此析构函数时,Foo
的对象将被销毁或完成
从析构函数显式返回意味着我们不想破坏它。
我想这样做,以便某个对象只能通过另一个对象析构函数销毁,即只有当另一个对象准备被销毁时才会被销毁。
示例:
class Class1{
...
Class2* myClass2;
...
};
Class1::~Class1(){
myClass2->status = FINISHED;
delete myClass2;
}
Class2::~Class2(){
if (status != FINISHED) return;
}
我在网上搜索,似乎无法找到我的问题的答案。 我也尝试通过使用调试器逐步完成一些代码来解决这个问题,但无法得到确定的结果。
答案 0 :(得分:45)
不,你不能阻止对象被return语句破坏,这只意味着dtor的主体的执行将在那一刻结束。之后它仍将被销毁(包括其成员和基地),并且内存仍将被释放。
你偏移抛出异常。
Class2::~Class2() noexcept(false) {
if (status != FINISHED) throw some_exception();
}
Class1::~Class1() {
myClass2->status = FINISHED;
try {
delete myClass2;
} catch (some_exception& e) {
// what should we do now?
}
}
请注意它确实是一个可怕的想法。你最好重新考虑一下这个设计,我相信一定有更好的设计。
修改强>
我犯了一个错误,抛出异常赢得了停止其基地和成员的破坏,只是让我们可以获得Class2
的dtor的处理结果。可以用它做什么仍然不明确。
答案 1 :(得分:24)
~Foo(){
return;
}
表示完全相同:
~Foo() {}
它类似于void
函数;在没有return;
语句的情况下到达终点与在结尾处return;
相同。
析构函数包含在销毁Foo
的过程已经开始时执行的操作。在不中止整个程序的情况下,不可能中止销毁过程。
答案 2 :(得分:17)
[D] oes明确地从析构函数返回意味着我们不想破坏它?
没有。提前返回(通过return;
或throw ...
)仅表示析构函数的其余部分未执行。基础和成员仍然被破坏,他们的析构函数仍在运行,请参阅[except.ctor]/3。
对于任何存储持续时间的类类型的对象,其初始化或销毁由异常终止,则为每个对象的完全构造的子对象调用析构函数...
请参阅下文,了解此行为的代码示例。
我想这样做,以便某个对象只能通过另一个对象析构函数销毁,即只有当另一个对象准备被销毁时才会被销毁。
听起来这个问题的根源在于所有权问题。仅在父母以非常常见的习语销毁并且使用(但不限于)之一实现时,删除“拥有”对象;
std::unique_ptr<>
std::shared_ptr<>
鉴于OP中的代码示例,std::unique_ptr<>
可能是合适的替代方案;
class Class1 {
// ...
std::unique_ptr<Class2> myClass2;
// ...
};
Class1::~Class1() {
myClass2->status = FINISHED;
// do not delete, the deleter will run automatically
// delete myClass2;
}
Class2::~Class2() {
//if (status != FINISHED)
// return;
// We are finished, we are being deleted.
}
我注意到示例代码中的if
条件检查。它暗示了国家与所有权和生命关系密切相关。它们并非都是一样的;当然,你可以将对象达到某个状态到它的“逻辑”生命周期(即运行一些清理代码),但我会避免直接链接到对象的所有权。重新考虑这里涉及的一些语义或允许“自然”构造和破坏来决定对象的开始和结束状态可能是一个更好的主意。
旁注;如果你必须检查析构函数中的某个状态(或断言某些结束条件),throw
的一个替代方法是调用std::terminate
(带有一些日志记录),如果不满足该条件。此方法类似于标准行为和结果,当由于已抛出的异常而展开堆栈时抛出异常。当std::thread
以未处理的异常退出时,这也是标准行为。
[D] oes明确地从析构函数返回意味着我们不想破坏它?
否(见上文)。以下代码演示了此行为; linked here和dynamic version。 需要noexcept(false)
才能避免std::terminate()
被调用。
#include <iostream>
using namespace std;
struct NoisyBase {
NoisyBase() { cout << __func__ << endl; }
~NoisyBase() { cout << __func__ << endl; }
NoisyBase(NoisyBase const&) { cout << __func__ << endl; }
NoisyBase& operator=(NoisyBase const&) { cout << __func__ << endl; return *this; }
};
struct NoisyMember {
NoisyMember() { cout << __func__ << endl; }
~NoisyMember() { cout << __func__ << endl; }
NoisyMember(NoisyMember const&) { cout << __func__ << endl; }
NoisyMember& operator=(NoisyMember const&) { cout << __func__ << endl; return *this; }
};
struct Thrower : NoisyBase {
Thrower() { cout << __func__ << std::endl; }
~Thrower () noexcept(false) {
cout << "before throw" << endl;
throw 42;
cout << "after throw" << endl;
}
NoisyMember m_;
};
struct Returner : NoisyBase {
Returner() { cout << __func__ << std::endl; }
~Returner () noexcept(false) {
cout << "before return" << endl;
return;
cout << "after return" << endl;
}
NoisyMember m_;
};
int main()
{
try {
Thrower t;
}
catch (int& e) {
cout << "catch... " << e << endl;
}
{
Returner r;
}
}
具有以下输出;
NoisyBase
NoisyMember
Thrower
before throw
~NoisyMember
~NoisyBase
catch... 42
NoisyBase
NoisyMember
Returner
before return
~NoisyMember
~NoisyBase
答案 3 :(得分:8)
根据C ++标准(12.4 Destructors)
8执行析构函数体后破坏任何析构函数 在body中分配的自动对象,类X的析构函数 为X的直接非变量非静态数据调用析构函数 成员,X的直接基类的析构函数,如果X是 最派生类的类型(12.6.2),它的析构函数调用 X的虚拟基类的析构函数。所有的析构函数都被称为 好像它们是用限定名称引用的,即忽略 更多派生类中任何可能的虚拟覆盖析构函数。 基地和成员按完成的相反顺序销毁 他们的构造函数(见12.6.2)。 a中的返回声明(6.6.3) 析构函数可能不会直接返回调用者;之前 将控制权转移给呼叫者,成员的析构者 和基地被称为。调用数组元素的析构函数 按其构造的相反顺序(见12.6)。
因此,返回语句不会阻止调用析构函数的对象被破坏。
答案 4 :(得分:7)
明确地从析构函数返回意味着我们不想破坏它。
没有。
析构函数是一个函数,因此您可以在其中使用return
关键字,但是这不会阻止对象的破坏,一旦您在析构函数内部,您已经在摧毁对象,所以任何想要防止以前必须发生的逻辑。
出于某种原因,我直觉地认为您的设计问题可以通过shared_ptr
和自定义删除程序来解决,但这需要有关该问题的更多信息。
答案 5 :(得分:1)
无论你在析构函数中return
多久,内部所有基于堆栈的对象都将被破坏。如果您错过了delete
动态分配的对象,那么会有故意内存泄漏。
这就是 move-constructors 如何运作的全部想法。移动CTOR只会占用原始对象的内存。原始对象的析构函数根本不会/不能调用delete
。
答案 6 :(得分:1)
没有。 return
只是意味着退出方法,它不会停止对象的破坏。
另外,你为什么要这样做?如果对象在堆栈上分配并且您以某种方式设法停止销毁,那么该对象将存在于堆栈的回收部分,该部分可能会被下一个函数调用覆盖,该函数调用可能会遍历您的对象内存并创建未定义的行为
同样,如果对象是在堆上分配的,并且您设法防止破坏,则会因为调用delete
的代码假定它不需要保留而导致内存泄漏一个指向该对象的指针,当它实际上仍在那里并占用没有人引用的内存时。
答案 7 :(得分:1)
你可以制作一种新方法来使对象自杀&#34;并保持析构函数为空,所以这样的事情将完成你想做的工作:
Class1::~Class1()
{
myClass2->status = FINISHED;
myClass2->DeleteMe();
}
void Class2::DeleteMe()
{
if (status == FINISHED)
{
delete this;
}
}
Class2::~Class2()
{
}
答案 8 :(得分:1)
当然不是。显式调用&#39; return&#39;在执行析构函数后,ist 100%等效于隐式返回。
答案 9 :(得分:1)
因此,正如所有其他人所指出的那样,return
不是解决方案。
我要补充的第一件事是你通常不应该担心这一点。除非你的教授明确要求。
如果你不相信外部类只能在合适的时间删除你的课程,那将是非常奇怪的,我认为没有其他人看到它。
如果指针被传递,指针很可能是shared_ptr
/ weak_ptr
,并让它在适当的时刻销毁你的类。
但是,嘿,如果我们学到了什么(并且在截止日期之前不浪费时间,我们很想知道如果它出现了,我们将如何解决一个奇怪的问题!)
那么解决方案呢?如果你至少可以信任Class1的析构函数而不是过早地破坏你的对象,那么你可以将Class2的析构函数声明为private,然后将Class1的析构函数声明为Class2的朋友,如下所示:
class Class2;
class Class1 {
Class2* myClass2;
public:
~Class1();
};
class Class2 {
private:
~Class2();
friend Class1::~Class1();
};
Class1::~Class1() {
delete myClass2;
}
Class2::~Class2() {
}
作为奖励,您不需要'status'标志;这是好事 - 如果有人想要这么糟糕的话,为什么不在其他任何地方将状态标志设置为FINISHED
,然后拨打delete
?
这样,您可以实际保证除了Class1的析构函数之外,其他任何地方都可以销毁该对象。
当然,Class1的析构函数可以访问Class2的所有私有成员。这可能没关系 - 毕竟,Class2即将被摧毁!但如果确实如此,我们可以想出更复杂的方法来解决它;为什么不。例如:
class Class2;
class Class1 {
private:
int status;
Class2* myClass2;
public:
~Class1();
};
class Class2Hidden {
private:
//Class2 private members
protected:
~Class2Hidden();
public:
//Class2 public members
};
class Class2 : public Class2Hidden {
protected:
~Class2();
friend Class1::~Class1();
};
Class1::~Class1() {
delete myClass2;
}
Class2Hidden::~Class2Hidden() {
}
Class2::~Class2() {
}
这样,公共成员仍然可以在派生类中使用,但私有成员实际上是私有的。 ~Class1只能访问Class2的私有和受保护成员,以及Class2Hidden的受保护成员;在这种情况下,它只是析构函数。 如果你需要保护Class2的受保护成员免受Class1的析构函数的攻击......有办法,但这实际上取决于你在做什么。
祝你好运!答案 10 :(得分:0)
对于这种情况,您可以使用delete运算符的特定于类的重载。 所以对你来说Class2你可以这样吗
class Class2
{
static void operator delete(void* ptr, std::size_t sz)
{
std::cout << "custom delete for size " << sz << '\n';
if(finished)
::operator delete(ptr);
}
bool Finished;
}
然后,如果在删除之前将finish设置为true,则将调用实际删除。 请注意,我还没有测试过,我只是修改了我在这里找到的代码 http://en.cppreference.com/w/cpp/memory/new/operator_delete
Class1::~Class1()
{
class2->Finished = true;
delete class2;
}