显式删除析构函数而不调用delete
我正在阅读 C++11 FAQ 并注意到这一点:
类 X4 { ~X4() = 删除; // 禁止破坏 }
这也隐含地禁止移动 X4。允许复制,但不推荐使用。
我还找到了这段引用。
删除析构函数的定义将需要在自由存储上进行分配,因为静态和自动对象隐式调用析构函数:`
<前><代码>结构C { ~C()=删除; //防止自动和静态对象 };但是,这种技术并不像看起来那么有用,因为它也会阻止删除表达式。不过,单例对象可以使用它。`
这是有道理的。我的问题是,使用带有显式删除析构函数的单例是否被认为是好的做法?另外,如果有人知道您不应该调用 delete
的任何其他情况,请告诉我。
I was reading the C++11 FAQ and noticed this:
class X4 { ~X4() = delete; // Disallow destruction }
This implicitly also disallows moving of X4s. Copying is allowed, but deprecated.
I also found this quote.
Deleting the definition of a destructor will require allocation on the free-store because static and automatic objects implicitly invoke the destructor:`
struct C { ~C()= delete; //prevent automatic and static objects };
However, this technique is less useful than it may seem because it prevents delete expressions, too. Singleton objects can use it, though.`
Which makes sense. My question is, is it considered good practice have a Singleton with an explicitly deleted destructor? Also, if anyone knows of any other case scenarios were you shouldn't call delete
, please let me know.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
实际上,您有时可能会发现自己处于销毁特定类型对象不安全的情况。因此,您需要删除析构函数以防止任何人尝试。
对于单例来说,该类型永远只有一个实例,与大量实例未清理相比,未能销毁它的危害可能要小一些。
单例(或任何其他全局可用的对象)的问题之一是您可能会失去对它们的依赖项的控制。那么很难制定出安全的销毁顺序 - 如果全局数据库对象记录到您已安全关闭连接的全局记录器对象,但可选地全局记录器对象通过全局数据库对象将其记录到数据库,那么你就有问题了。
尽管您可能通过不破坏全局数据库对象来“解决”这个问题,但我认为这不应该真正被称为“良好实践”。这听起来更像是一种无需重新设计即可处理不良情况的简单方法(尽管在我的示例中,重新设计也可能非常简单 - 只需确保数据库记录器或数据库本身对日志消息执行一些有用的操作即可)当连接关闭时吞掉它们,或将它们重定向到另一个可用的目的地)。
可能存在“好的”设计,其中特定类型的对象不能被破坏,但这不是 C++ 类的通常设计方式。
Pragmatically, you might sometimes find yourself in a situation where it's unsafe to destroy objects of a particular type. So you'd delete the destructor to prevent anyone trying.
In the case of a Singleton, where there's only ever going to be one instance of the type, failing to destroy it might be less harmful than if there are loads of instances not cleaned up.
One of the problems with Singletons (or any other globally-available object) is that you can lose control of the dependencies on them. Then it's hard to work out a safe order of destruction -- if the global database object logs to the global logger object that you've closed the connection safely, but optionally the global logger object does its logging to the database via the global database object, then you have a problem.
Although you might "solve" this problem by not destroying the global database object, I don't think this should really be called "good practice". It sounds more like a simple way to deal with a bad situation without re-design (although in my example the redesign might be pretty simple too - just make sure that either the DB logger, or the DB itself, does something useful with log messages when the connection is closed. Swallow them, or redirect them to another available destination).
It may be that there are "good" designs in which a particular type of object cannot be destroyed, but it's not the usual way C++ classes are designed.
内存池的使用是我能想到的一种场景。
Usage of a memory pool is one scenario I can think of.
有一种情况永远不会调用析构函数,即使对于自动变量也是如此:当显式析构函数
X::~X
时,类X
内匿名联合的析构函数> 由用户编写。由于 union 是匿名的,X::~X
根本无法调用它的析构函数(现在不知道如何调用析构函数,因为它确实不知道什么调用析构函数)。顺便说一句,在这种情况下,用户无法声明要删除的联合析构函数(同样是因为缺少名称),但可以隐式删除它。
奇怪的是,在这种情况下,默认析构函数
X::~X
会调用匿名联合的析构函数。然而,只要允许,这就是一个纯粹的形式问题,析构函数调用没有任何作用。这是因为只有当联合体的所有变体都具有简单的析构函数(因此联合体本身也如此)时才允许这样做;如果它们中的任何一个具有非平凡的析构函数,则联合的析构函数将被隐式删除,从而使X
的默认析构函数不可操作(有效删除)。然而,这并不意味着不能使用包含匿名联合的类
X
,该联合至少具有一个具有重要析构函数的成员。它只是意味着用户编写的 X::~X 必须直接析构匿名联合体的活动变体,绕过联合体本身已删除的析构函数。这是可能的,前提是该类包含允许知道哪个变体处于活动状态的附加成员。类似地,X
的构造函数应该直接构造最多一个联合变体,绕过匿名联合的(可能已删除)构造函数(除非变体是 POD,这样的构造函数不应该 相反,直接分配给联合的变体)。事实上,匿名联合体的特殊成员函数是一种幻影实体,它不可能是不平凡的,其唯一的作用是,通过可能被删除,有效地将删除状态传播到包含的相应特殊成员函数。 >X。
There is one situation where a destructor will never be called, even for automatic variables: the destructor for an anonymous union inside a class
X
, when an explicit destructorX::~X
is written by the user. Since the union is anonymous, there is simply no way that its destructor can be called byX::~X
(it does not now how to call the destructor because it does not know what to call the destructor).Incidentally, in this situation there is no way for the user to declare the union destructor to be deleted (again for lack of a name), but it can be implicitly deleted.
Curiously in this situation the default destructor
X::~X
would have called the destructor for the anonymous union. However, whenever allowed this is a purely formal matter and the destructor call has no effect. This is because this is only allowed if all variants of the union have trivial destructors (and therefore the union itself as well); if any of them has a non-trivial destructor, then the destructor of the union is implicitly deleted, making the default destructor ofX
inoperable (effectively deleted).However this does not mean that one cannot use a class
X
containing an anonymous union with at least one member with a non-trivial destructor. It just means that the user-writtenX::~X
must directly destruct the active variant of the anonymous union, bypassing the deleted destructor of the union itself. This is possible, provided the class contains additional members that allow knowing which variant is active. Similarly constructors ofX
should directly construct at most one variant of the union, bypassing the (possibly deleted) constructor of the anonymous union (unless the variant is POD, such a constructor should not instead directly assign to a variant of the union). In fact the special member functions of an anonymous union are a kind of phantom entities, which cannot be nontrivial, and whose only role is, by possibly being deleted, to effectively propagate that deleted status to the corresponding special member function of the containingX
.