带有return的C ++析构函数
在C ++中,如果我们定义一个类的析构函数为:
~Foo(){ return; }
一旦调用这个析构函数, Foo
的对象就会被销毁,或者从析构函数中显式返回,这意味着我们永远不会想要销毁它。
我想这样做只有通过另一个对象析构函数才能销毁某个对象,也就是说只有当另一个对象准备销毁时才能销毁它。
例:
class Class1{ ... Class2* myClass2; ... }; Class1::~Class1(){ myClass2->status = FINISHED; delete myClass2; } Class2::~Class2(){ if (status != FINISHED) return; }
我在网上search,似乎无法find我的问题的答案。 我也尝试通过一些代码一步一步地debugging,但不能得出一个结论性的结果。
不,你不能阻止对象被return语句所销毁,这只是意味着这个对象的执行将会在这个点结束。 之后还会被摧毁(包括其成员和基地),记忆仍然会被释放。
你migth抛出exception。
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? } }
注意这确实是一个可怕的想法。 你最好重新考虑这个devise,我相信肯定会有更好的devise。
编辑
我犯了一个错误,抛出exception不会阻止它的基地和成员的破坏,只是使得获得Class2
的过程结果成为可能。 而对此可以做些什么还不清楚。
~Foo(){ return; }
的意思完全相同:
~Foo() {}
它类似于一个void
函数; 达到目的而没有return;
声明与return;
相同return;
最后。
析构函数包含在销毁Foo
的过程已经开始时执行的操作。 如果不中止整个计划,就不可能放弃销毁过程。
显式从析构函数返回的意思是说我们不想破坏它?
不。早期返回(通过return;
或者throw ...
)只意味着析构函数的其余部分不被执行。 基地和成员仍然被摧毁,他们的破坏者仍然运行,见[except.ctor] / 3 。
对于初始化或销毁由exception终止的任何存储持续时间的类types的对象,将为每个对象的完全构造的子对象调用析构函数。
请参阅下面的代码示例这种行为。
我想这样做只有通过另一个对象析构函数才能销毁某个对象,也就是说只有当另一个对象准备销毁时才能销毁它。
听起来这个问题根源于所有权问题。 删除“拥有”的对象,只有一个非常常见的习惯用母语销毁,并与(但不限于)其中之一达成;
- 组成,它是一个自动的成员variables(即“基于堆栈”)
-
std::unique_ptr<>
来表示dynamic对象的独占所有权 -
std::shared_ptr<>
来表示dynamic对象的共享所有权
给定OP中的代码示例, std::unique_ptr<>
可能是一个合适的select;
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
(有一些日志logging)。 这种方法与标准的行为和结果类似,当由于已经抛出的exception而导致堆栈展开时抛出exception。 这也是标准的行为,当一个std::thread
退出一个未处理的exception。
显式从析构函数返回的意思是说我们不想破坏它?
否(见上文)。 以下代码演示了这种行为; 这里链接和一个dynamic版本 。 需要使用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
根据C ++标准(12.4析构函数)
8执行析构函数的主体并销毁在主体内分配的任何自动对象后,X类的析构函数调用X的直接非variables非静态数据成员的析构函数,X的直接基类的析构函数,如果X是大多数派生类(12.6.2)的types,其析构函数调用X的虚拟基类的析构函数。 所有的析构函数都被调用,就好像它们被引用了一个限定的名字,也就是说忽略了更多派生类中的任何可能的虚拟覆盖析构函数。 基地和成员按照完成build造者的相反顺序销毁(见12.6.2)。 析构函数中的返回语句(6.6.3)可能不会直接返回给调用者。 在将控制转移给调用者之前,调用成员和基础的析构函数。 数组元素的析构函数按照其构造的相反顺序调用 (见12.6)。
所以返回语句并不能阻止析构函数被调用的对象被销毁。
显式从析构函数返回意味着我们永远不想破坏它。
没有。
析构函数是一个函数,所以你可以在里面使用return
关键字,但是不会阻止对象的破坏,一旦你在析构函数中,你已经销毁了你的对象,所以任何想要阻止它的逻辑将不得不发生在之前。
出于某种原因,我直觉地认为你的devise问题可以通过shared_ptr
来解决,也可以使用自定义的删除器,但是这需要更多关于上述问题的信息。
不pipe从析构函数return
多less,所有基于堆栈的对象都会被破坏。 如果你错过了delete
dynamic分配的对象,那么会有故意的内存泄漏。
这是移动构造器如何工作的整个想法。 移动CTOR只会把原始对象的内存。 原始对象的析构函数根本不会/不能调用delete
。
不, return
只是意味着退出方法,它不会停止对象的销毁。
另外,你为什么要? 如果对象被分配到堆栈上,并且以某种方式设法停止了销毁,那么该对象将生活在堆栈的回收部分上,这可能会被下一个函数调用覆盖,这可能会写入整个对象的内存,并创build未定义的行为。
同样,如果对象被分配在堆上,并且设法防止被破坏,那么你会有内存泄漏,因为调用delete
的代码会假定它不需要保持指向对象的指针,而实际上它仍然存在并占用无人参考的记忆。
当然不是。 显式调用“返回”ist 100%相当于执行析构函数后隐式返回。
你可以创build一个新的方法来使对象“自杀”,并保持析构空,所以这样的事情会做你想做的工作:
Class1::~Class1() { myClass2->status = FINISHED; myClass2->DeleteMe(); } void Class2::DeleteMe() { if (status == FINISHED) { delete this; } } Class2::~Class2() { }
所以,正如所有其他人所指出的那样, return
不是一个解决scheme。
我要补充的第一件事是你通常不应该担心这个。 除非你的教授明确要求。 如果你不能相信外部的class级只能在适当的时候删除你的class级,我觉得没有其他人能看到它。 如果指针被传递,指针将很可能是shared_ptr
/ weak_ptr
,并让它在适当的时候摧毁你的类。
但是,嘿,好奇的是,如果我们学到了一些东西(而不是在截止date之前浪费时间的话),我们如何解决一个奇怪的问题?
那么一个解决scheme是什么? 如果你至less可以相信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() { }
作为奖励,你不需要“状态”标志; 这是很好的 – 如果有人想要这个坏的拧你,为什么不设置状态标志完成其他地方,然后调用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析构函数的阻碍,那么有办法,但这取决于您在做什么。
祝你好运!
对于这种情况,您可以使用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; }
然后,如果在删除之前将完成设置为true,则实际删除将被调用。 请注意,我没有testing过,我只是修改了我在这里find的代码http://en.cppreference.com/w/cpp/memory/new/operator_delete
Class1::~Class1() { class2->Finished = true; delete class2; }