强制使用虚析构函数?C++

16

在C++ Faq lite中我没有看到关于这个问题的答案:

如何定义一个基类,使得每个继承它的类都必须定义一个析构函数?

我尝试运行了这个程序

struct VDtor { virtual ~VDtor()=0;  };
struct Test:VDtor { virtual ~Test(){}  };
int main() { delete new Test; return 0; }

http://codepad.org/wFcE71w3 出现了错误

In function `Test::~Test()':
t.cpp:(.gnu.linkonce.t._ZN4TestD0Ev+0x1e): undefined reference to `VDtor::~VDtor()'
In function `Test::~Test()':
t.cpp:(.gnu.linkonce.t._ZN4TestD1Ev+0x1e): undefined reference to `VDtor::~VDtor()'

那么,这是可能的吗?


17
对于这个可能是迄今为止最高效且自包含的示例代码,我给予加1,并感到非常满意。 :-D - DevSolar
1
为什么要强制用户编写更多的代码?如果你需要进行清理工作,那就自己动手去做,不要把负担放在用户身上;如果你不需要,那这样做就是无用的。你能提供一个实际有用的例子吗? - Matthieu M.
2
@acid 我不确定 @Matthieu 是怎么想的,但这对我来说完全没有意义 - Johannes Schaub - litb
3
代码中仍然有一个多余的 return 0; - Konrad Rudolph
2
@acidzombie24:不,你搞混了。main函数的返回类型必须int,而且(只有在C++中)不能省略,但是(只有在C++中)你不需要显式地写出return 0; - Konrad Rudolph
显示剩余16条评论
4个回答

17
在某种程度上是“可能”的(如果您的目标是派生类保持抽象的话)。但它不会产生您想要的结果:因为如果程序员没有这样做,编译器将隐式地创建一个析构函数。
因此,无法强制要求派生类的作者显式声明构造函数。
(编辑:就像@chubsdad指出的那样,您特定代码中的错误是因为需要定义基类的显式声明析构函数)。 编辑: 仅仅是为了好玩,有一些情况确实需要显式声明构造函数,请考虑以下情况。
struct Viral {
  struct Dose { };
protected:
  ~Viral() throw (Dose) { }
};

struct Base {
  virtual ~Base() throw() { }
};

struct Derived : Base, Viral { };

这段代码无法编译,因为隐式声明的~Derived将具有松散的异常规范throw (Dose),这比~Base所具有的更放宽 - 因此违反了覆盖者不得具有松散的异常规范的要求。您需要明确地声明适当的析构函数。

struct Derived : Base, Viral { ~Derived() throw() { } };

但这并不是你的问题的真正解决方案,因为派生类需要“合作”,要么继承自Viral,要么将其作为非静态数据成员。而且这样做很丑陋 :)


编辑:以下似乎是一种符合标准的方法

struct Viral {
  struct Dose { };
protected:
  ~Viral() throw (Dose) { }
};

struct Base : virtual Viral {
  virtual ~Base() throw() { }
};

从Clang和GCC(从v4.6开始)开始拒绝任何具有隐式声明析构函数的Base派生类,因为它具有不兼容的异常规范(任何派生类都必须直接调用 ~Viral 而不是通过调用 ~Base 间接调用,标准如此规定)。Comeau则接受这一点,但我强烈怀疑在这方面它是否符合标准。


1
+1:在回复这个问题时,我完全忘记了隐式析构函数。 - Chubsdad
这是否意味着不可能强制派生类定义一个析构函数? - user34537
1
@acid 我认为不可能强制程序员在派生类中明确定义一个析构函数。你可以告诉我需要这个的具体用途吗? - Johannes Schaub - litb
@Johannes Schaub - litb:抱歉,我删除了我的帖子以避免错误信息的混淆。也许你可以删除对我的帖子的引用。 - Chubsdad
你应该说“不行”而不是“可以”,因为我的问题是关于强制派生类定义析构函数,而这显然是不可能的。 - user34537
显示剩余3条评论

1
每个类都有一个析构函数,无论如何。在基类中声明虚拟析构函数可以确保子类具有虚拟析构函数。这并不意味着编码人员需要显式声明析构函数——无论如何都不是好事情。它只是意味着如果声明了析构函数,它将是虚拟的。

1
将基类的析构函数声明为虚函数也意味着任何派生类中隐式声明的析构函数都将是虚函数。正如您所说,这就是您所需要的全部。 - Fred Nurk
@Fred:是的,我在解释时应该更清楚地说明,即使是子类中隐式声明的析构函数也总是会是虚拟的。一旦是虚拟的,就永远是虚拟的。感谢您指出我在这里表达不清楚的问题。 - Steven Sudit
再仔细看一遍,我发现问题出在“如果声明了析构函数”这里:析构函数总是被声明的,无论是由程序员显式声明还是隐式由编译器生成。(这比我之前的评论更清晰明了。) - Fred Nurk
@Fred:是的,在我回答的最后一句话中,我应该说“明确声明”。 - Steven Sudit

1
struct VDtor { virtual ~VDtor()=0;  };
VDtor::~VDtor () { } // <== Implementation.
struct Test:VDtor { ~Test(){}  };
int main() { delete new Test; return 0; }

要修复错误,您必须像上面那样实际实现VDtor ::〜VDtor()。


但这并不强制派生类实现它们自己的析构函数。 - user34537
4
@acidzombie24:如果一个类需要非空的析构函数,那么基类无论做什么都无济于事;如果隐式的空析构函数已经足够了,那么明确指出来也没有意义。在我看来是这样的。 - wilx

0
当Test被销毁时,它将调用它的基类析构函数,但该函数并不存在。如果您没有必要的销毁逻辑,应该将其声明为空。

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