析构函数的使用=删除;

考虑以下课程:

struct S { ~S() = delete; }; 

不久,为了这个问题的目的:我不能像S s{};那样创buildS s{};实例S s{}; 因为我不能摧毁他们。
正如在评论中提到的,我仍然可以通过执行S *s = new S;来创build一个实例S *s = new S; ,但我不能删除它。
因此,我可以看到一个删除析构函数的唯一用法是这样的:

 struct S { ~S() = delete; static void f() { } }; int main() { S::f(); } 

也就是说,定义一个仅暴露一堆静态函数的类,并禁止任何尝试创build该类的实例。

删除析构函数的其他用途(如果有的话)是什么?

如果您有一个永远不会delete的对象,或者将其存储在堆栈中(自动存储),或将其作为另一个对象的一部分存储,则=delete将阻止所有这些对象。

 struct Handle { ~Handle()=delete; }; struct Data { std::array<char,1024> buffer; }; struct Bundle: Handle { Data data; }; using bundle_storage = std::aligned_storage_t<sizeof(Bundle), alignof(Bundle)>; std::size_t bundle_count = 0; std::array< bundle_storage, 1000 > global_bundles; Handle* get_bundle() { return new ((void*)global_bundles[bundle_count++]) Bundle(); } void return_bundle( Handle* h ) { Assert( h == (void*)global_bundles[bundle_count-1] ); --bundle_count; } char get_char( Handle const* h, std::size_t i ) { return static_cast<Bundle*>(h).data[i]; } void set_char( Handle const* h, std::size_t i, char c ) { static_cast<Bundle*>(h).data[i] = c; } 

这里我们有不透明的Handle ,它可能不是在堆栈中声明的,也不是dynamic分配的。 我们有一个系统来从一个已知的数组中获取它们。

我相信上面没有什么是不确定的行为; 不能销毁一个Bundle是可以接受的,就像创build一个新的Bundle一样。

而且界面不必暴露Bundle工作方式。 只是一个不透明的Handle

现在,如果代码的其他部分需要知道所有句柄都在特定的缓冲区中,或者它们的生命周期是以特定的方式进行跟踪的,那么这种技术会很有用。 可能这也可以通过私人构造函数和朋友工厂函数来处理。

一种情况可能是防止错误的释放:

 #include <stdlib.h> struct S { ~S() = delete; }; int main() { S* obj= (S*) malloc(sizeof(S)); // correct free(obj); // error delete obj; return 0; } 

这是非常基本的,但适用于任何特殊的分配/释放过程(例如工厂)

一个更“c ++”风格的例子

 struct data { //... }; struct data_protected { ~data_protected() = delete; data d; }; struct data_factory { ~data_factory() { for (data* d : data_container) { // this is safe, because no one can call 'delete' on d delete d; } } data_protected* createData() { data* d = new data(); data_container.push_back(d); return (data_protected*)d; } std::vector<data*> data_container; }; 

有两个似是而非的用例。 首先(如一些注释),dynamic分配对象是可以接受的,不能delete它们,并允许在程序结束时清理操作系统。

另外(甚至更奇怪)你可以分配一个缓冲区,并在其中创build一个对象,然后删除缓冲区来恢复的地方,但从来没有提示尝试调用析构函数。

 #include <iostream> struct S { const char* mx; const char* getx(){return mx;} S(const char* px) : mx(px) {} ~S() = delete; }; int main() { char *buffer=new char[sizeof(S)]; S *s=new(buffer) S("not deleting this...");//Constructs an object of type S in the buffer. //Code that uses s... std::cout<<s->getx()<<std::endl; delete[] buffer;//release memory without requiring destructor call... return 0; } 

除了在特殊情况下,这些都不是好主意。 如果自动创build的析构函数什么也不做(因为所有成员的析构函数都是微不足道的),那么编译器将创build一个无效的析构函数。

如果自动创build的析构函数会做一些不重要的事情,那么很可能由于未能执行它的语义而损害程序的有效性。

让一个程序离开main()并允许环境“清理”是一种有效的技术,但是最好避免,除非约束条件是绝对必要的。 充其量,这是一个掩盖真正的内存泄漏的好方法!

我怀疑这个function是否完整,能够delete其他自动生成的成员。

我很想看到这个function的实际使用。

有一个静态类的概念(没有构造函数),所以逻辑上不需要析构函数。 但是这样的类更适当的实现,因为namespace在现代C ++中没有(好的)地方,除非是模板化的。

为什么要将析构函数标记为delete

为了防止析构函数被调用,当然;)

什么是用例?

我可以看到至less 3种不同的用途:

  1. 该类不应该被实例化; 在这种情况下,我也希望删除默认的构造函数。
  2. 这个类的一个实例应该被泄露; 例如,一个日志logging单例实例
  3. 这个类的一个实例只能由一个特定的机制来创build和处理; 这在使用FFI时可能会发生

为了说明后一点,设想一个C接口:

 struct Handle { /**/ }; Handle* xyz_create(); void xyz_dispose(Handle*); 

在C ++中,你会想把它包装在一个unique_ptr来自动发布,但是如果你不小心写了: unique_ptr<Handle> ? 这是一个运行时间的灾难!

相反,你可以调整类的定义:

 struct Handle { /**/ ~Handle() = delete; }; 

然后编译器会扼杀unique_ptr<Handle>强制您正确使用unique_ptr<Handle, xyz_dispose>

new创build一个对象的实例并且永远不删除它是实现一个C ++ Singleton的最安全的方法,因为它避免了任何和所有的销毁顺序问题。 这个问题的一个典型例子就是在另一个Singleton类的析构函数中访问的“Logging”单例。 Alexandrescu曾经在他的古典“现代C ++devise”一书中讨论了如何应对Singleton实现中的破坏顺序问题。

删除析构函数是很好的,所以即使是Singleton类本身也不能不小心删除实例。 它还可以防止像delete &SingletonClass::Instance()这样的疯狂用法(如果Instance()返回一个引用,就像它应该;没有理由返回一个指针)。

尽pipe如此,没有什么值得注意的。 当然,无论如何,你不应该首先使用单身。