明确删除析构函数且不调用delete

6

我在阅读C++11 FAQ时发现了这个问题:

class X4 {
    ~X4() = delete; // Disallow destruction
}

This implicitly also disallows moving of X4s. Copying is allowed, but deprecated.

我还找到了这句话

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.`

这很有道理。我的问题是,使用一个显式删除析构函数的单例模式是否被认为是良好的实践呢?此外,如果有人知道任何其他情况下不应该调用delete的情况,请告诉我。


一个普遍问题的普遍回答:在C++中,操作系统永远不会为您清理,它不是那种框架。您始终需要以某种方式编写删除代码。但这显然不是您想要的答案... - Hannes R.
如果你修正这个问题,问是否将单例类的析构函数定义为删除是否被认为是良好的实践,那么你肯定会得到一大堆回复,质疑单例本身的合法性。 - Kerrek SB
2
为什么你想要在单例模式中使用它? - Martin York
@KerrekSB:提出问题并不容易!不过还是谢谢你的建议。 - Jesse Good
2
有一个单例在任何情况下都是良好的实践吗? - Puppy
显示剩余4条评论
3个回答

2
实际情况下,有时您可能会发现销毁特定类型的对象是不安全的。因此,您可以删除析构函数以防止任何人尝试这样做。
在单例模式的情况下,只会有一个实例存在,如果未能销毁它,可能比存在大量未清理的实例更少造成伤害。
单例模式(或任何其他全局可用对象)的问题之一是您可能会失去对它们的依赖控制。然后很难确定销毁的安全顺序——如果全局数据库对象记录到全局日志记录器对象已安全关闭连接,但是可选地,全局日志记录器对象通过全局数据库对象将其日志记录到数据库中,则会出现问题。
虽然您可能通过不销毁全局数据库对象来“解决”此问题,但我认为这真的不能称为“良好的实践”。这听起来更像是一种处理糟糕情况而不需要重新设计的简单方法(尽管在我的示例中,重新设计可能也非常简单——只需确保DB记录器或DB本身在关闭连接时对日志消息执行某些有用操作。吞咽它们或将它们重定向到另一个可用目标)。
可能存在“良好”的设计,其中无法销毁特定类型的对象,但这不是C ++类通常设计的方式。

1
同时,如果有人知道任何其他情况下不应调用删除的案例,请告诉我。使用内存池是我能想到的一种情况。

1
怎么做?似乎你可以调用一个自定义实现的 delete (struct X { void operator delete[] (void*) {} };)。 - sehe

0

有一种情况下,即使是自动变量,析构函数也永远不会被调用:当用户编写了显式析构函数X::~X时,类X中的匿名联合体的析构函数就会出现这种情况。由于联合体是匿名的,所以它的析构函数根本无法被X::~X调用(因为它不知道如何调用析构函数,因为它不知道要调用哪个析构函数)。

顺便说一句,在这种情况下,用户无法声明联合体的析构函数已删除(同样是因为缺少名称),但可以隐式删除。

奇怪的是,在这种情况下,默认析构函数X::~X将调用匿名联合体的析构函数。然而,只要允许,这只是一个纯粹的形式问题,析构函数调用没有任何效果。这是因为只有在联合体的所有变体都具有平凡析构函数(因此联合体本身也是如此)的情况下才允许这样做;如果其中任何一个具有非平凡析构函数,则联合体的析构函数将被隐式删除,使得X的默认析构函数无法操作(实际上被删除)。

然而,这并不意味着不能使用包含具有非平凡析构函数的匿名联合体的类X。它只是意味着用户编写的X::~X必须直接销毁匿名联合体的活动变量,绕过联合体本身的已删除析构函数。这是可能的,前提是类包含其他成员,允许知道哪个变量是活动的。同样,X的构造函数应该直接构造联合体的最多一个变体,绕过匿名联合的(可能已删除的)构造函数(除非变体是POD,在这种情况下,这样的构造函数不应该,而是直接分配给联合的一个变体)。实际上,匿名联合的特殊成员函数是一种幻影实体,不能是非平凡的,其唯一作用是通过可能被删除的方式有效地将该已删除状态传播到包含X的相应特殊成员函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接