接口和-Wctor-dtor-privacy

时间:2013-12-12 14:43:30

标签: c++

我刚刚在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.而烦恼。如果没有正确使用该类,可能会导致一些错误吗?

3 个答案:

答案 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方法,其析构函数应为virtualprivateprotected

这通常是所教的内容(更容易记住),即使它有时太简单了。

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链接:

  1. 讨论的重点是抽象类,因此它们应该是创建抽象类的最少量的代码。请注意,例如,类型名称是如何毫无意义的,这在生产代码中可能是非常糟糕的!
  2. 该类完全可用原样,只有当某人在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。

我避免接口(其中许多是代码打击)