我刚刚在makefile中添加了Wctor-dtor-privacy
和-Wnon-virtual-dtor
选项,我的一个文件中出现错误。我通常为我的基类编写私有非虚拟析构函数或公共虚拟析构函数。但是我收到了一个关于我的一个接口的警告:
class TestInterface
{
public:
virtual void Send( const std::string& sendMessage ) = 0;
virtual std::string PopMessage() = 0;
virtual std::string GetLatestString() = 0;
virtual std::string::size_type GetReceivedBufferSize() const = 0;
};
warning: all member functions in class ‘TestInterface’ are private [-Wctor-dtor-privacy]
warning: ‘TestInterface’ has virtual functions and accessible non-virtual destructor [-Wnon-virtual-dtor]
我理解这个警告,我倾向于为我的所有接口编写一个公共虚拟析构函数,这很好。但我不明白为什么很多网站(有些教授CPP)都不会为此e.g.而烦恼。如果没有正确使用该类,可能会导致一些错误吗?
答案 0 :(得分:3)
通过声明虚拟方法,您可以指示某些子类将覆盖这些方法。如果您不提供虚拟析构函数,则通过指针删除子类将导致undefinde行为。见
When to use virtual destructors?
或
http://www.parashift.com/c++-faq/virtual-dtors.html
可能会因为过于关注特定方面或导致无法解决的行为而被忽略。
答案 1 :(得分:2)
当您在指针上调用delete
时,需要调用派生程度最高的对象(类型D
)的析构函数;但是编译器只知道如何调用它目前看到的静态类型(B
)的析构函数,所以如果那些类型不匹配(B!= D),那么{ {1}}需要一个虚拟析构函数。
这有几个含义:
B
规则的简化就是说:
void stack_allocated() {
MostDerived md; // no need for a virtual destructor,
... // the static type (MostDerived) is the actual type.
}
void dynamically_allocated() {
Derived* d = new MostDerived;
delete d; // Derived needs a virtual destructor,
// because Derived != MostDerived
// Bases of Derived need no virtual destructor
}
方法virtual
方法,其析构函数应为virtual
,private
或protected
这通常是所教的内容(更容易记住),即使它有时太简单了。
virtual
符合此简化。
注意:如果类已经有-Wnon-virtual-dtor
方法,那么制作析构函数virtual
通常没有任何代价,因为当前实现使用虚拟指针指向单个静态表,该表重新组合所有班级的方法。此外,如果调用能够预测动态类型,则编译器可能能够对调用进行虚拟化,在这种情况下它甚至没有运行时成本。
还有另一种选择:我开发了virtual
来警告不在声明网站,而是在呼叫网站(-Wdelete-non-virtual-dtor
这里)。因此,它没有误报(假设您使用delete
来标记叶类),但稍后会发出警告。 YMMV。
注意:例如,final
即使没有虚拟析构函数也没问题,因为std::shared_ptr<Derived> p = std::make_shared<MostDerived>();
会根据创建的实际类型创建一个Deleter;这也是为什么你可以std::make_shared
没有问题的原因。
关于您提供的IBM链接:
std::shared_ptr<void> p = ...;
上调用delete
时才会遇到麻烦。答案 2 :(得分:1)
接口是从其他编程语言(例如:.NET系列)派生的构造。在C ++中没有接口,而是一个抽象基类。
拥有模式:
class Derived : public Base, public Interface, Interface, ...
其中基类具有虚拟析构函数而不是(!)通过任何接口销毁,但仅基类,将使虚拟(接口)析构函数过时。
但是,任何C ++类都有一个析构函数,并且可能有一个接口IDisposable,而且每个其他接口都应该从它派生出来:
class IDisposable { public: virtual IDisposable() {}; };
注意:析构函数不是抽象的!
然而,这违背了接口的概念,因此:
class IDisposable { public: virtual IDisposable() = 0; };
这使得任何具体类中的手写析构函数成为强制性的! (删除没有虚拟析构函数的基类,使其失败) 回到.Net系列并拥有一个c ++ / cli类,它隐含着一个IDisposable。
我避免接口(其中许多是代码打击)