【问题标题】:Class has virtual method but non virtual destructor C++ [duplicate]类具有虚拟方法但非虚拟析构函数C ++ [重复]
【发布时间】:2012-09-21 18:06:38
【问题描述】:

可能重复:
GNU compiler warning “class has virtual functions but non-virtual destructor”

我正在为两个类编写一个接口,并在标题中收到警告。 代码如下:

class GenericSymbolGenerator {
   protected:                     // <== ok 
    ~GenericSymbolGenerator(void) {}

   public:
    virtual GenericSymbolTableCollection* generateSymbolTableCollection(GenericSymbolTableCollection *gst) = 0;
    GenericSymbolGenerator(void) {}
    // ~GenericSymbolGenerator(void) {} // <== warning if used
};

class PascalPredefinedSymbolGenerator : public GenericSymbolGenerator {
   protected:
    ~PascalPredefinedSymbolGenerator(void) {} // <== ok

   public:
    GenericSymbolTableCollection* generateSymbolTableCollection(GenericSymbolTableCollection *pst); // initializes *pst
    PascalPredefinedSymbolGenerator(void) {}
    // ~PascalPredefinedSymbolGenerator(void) {} <== warning if used
};

class PascalSymbolGenerator : public GenericSymbolGenerator {
    protected:
         ~PascalSymbolGenerator(void) {} // <== ok

    public:
     GenericSymbolTableCollection* generateSymbolTableCollection(GenericSymbolTableCollection *st); // initializes st
     PascalSymbolGenerator(void) {}
     // ~PascalSymbolGenerator(void) {} // <== warning if used
};

只要构造函数/析构函数是无效的,将析构函数声明为受保护就没有问题。当类使用堆时会出现问题(析构函数被声明为受保护的,无法将类从“外部”中释放出来,使对象“坚不可摧”)。 有没有更方便的方法(除了一路公开)?

【问题讨论】:

  • 为什么要保护析构函数?
  • 这只是界面必须完全对用户进行限制的情况。
  • > 你能进一步澄清你的问题吗? > 为什么你想要一个公共构造函数和一个受保护的析构函数? > 由于析构函数应该是虚拟的.. 这样当你销毁子类的 Object 时,它会调用基类的析构函数。
  • 这只是一个例子(我的接口构造函数/析构函数都是无效的)。我对他们不感兴趣的案例感兴趣。
  • @Sebi,我不太确定我是否遵循您的推理。您是说要将对象层次结构的使用限制为仅允许使用派生类的实例吗?如果是这样,使析构函数受保护是错误的方法。

标签: c++ eclipse methods virtual


【解决方案1】:

用作多态基的类应该有一个虚拟析构函数或一个受保护的析构函数。

原因是,如果您有一个公共的、非虚拟的析构函数,那么该类的外部用户几乎对它的任何使用都是不安全的。例如:

GenericSymbolGenerator *ptr = new PascalPredefinedSymbolGenerator();
delete ptr; // behavior is undefined, we tried to call the base class destructor

通过标记析构函数protected,您可以防止用户通过基类删除PascalPredefinedSymbolGenerator 对象。通过公开析构函数和virtual,当用户通过基类删除时,您可以获得定义的行为。所以选择其中一个选项。

【讨论】:

  • 声明析构函数虚拟在我的情况下更有用。将析构函数声明为受保护不会阻止实例化的派生对象被删除吗?
  • @Sebi:不,它只会阻止它们通过指向基址的指针被删除。它们仍然可以通过指向派生的指针删除。如果您的派生类对用户隐藏,用户只需调用一些工厂函数,返回一个智能指针,知道将其删除为什么类型——那么您将使用 protected 析构函数。
  • @Sebi FWIW,这是您问题的正确答案,而不是您选择的答案。
  • @Steve 谢谢,我现在知道了。
【解决方案2】:

我认为编译器正确地警告您基类中的非虚拟析构函数是正确的。您创建了一个明确打算作为继承层次结构的根的类,但是通过使基类析构函数成为非虚拟的,您破坏了通过指向基类的指针删除对象的能力(因为所有这些都是execute 是基类的析构函数,因此不会执行派生类特定的操作)。这在 C++ 中被认为是一个非常糟糕的主意,因为当涉及到这个对象层次结构时,您基本上会破坏多态性的实现。

正如您在 cmets 中提到的,您的意图是使用 GenericSymbolGenerator 作为接口并强制用户实例化和使用包含实际实现代码的派生类。在 C++ 中声明接口的规范方法是将接口上的至少一个函数声明为纯虚函数。这禁止您实例化基类,但仍创建可实例化的派生类。您已经通过在基类中将generateSymbolTableCollection() 声明为纯虚函数来完成此操作。因此,您需要做的就是将析构函数设为虚拟,因为在这个特定场景中它确实必须是虚拟的。

另外,顺便说一句,默认构造函数和析构函数的规范签名通常不使用“void”作为参数,而是使用空括号。

【讨论】:

  • 使析构函数受保护(或私有)意味着客户端代码永远不能通过基类指针删除派生类对象。所以在这种情况下不需要虚拟析构函数。请参阅链接的欺骗。
  • 我已经经历了欺骗。那里的析构函数也是 void。这就是我问一个新问题的原因。
  • @Sebi destructor is void 是什么意思?你的意思是它是微不足道的,即身体是空的?
  • 它有默认实现(不改变类成员/内存)。
  • -1,因为deleteing 基指针是 UB,而不是半破坏。
猜你喜欢
  • 2012-04-18
  • 2017-08-13
  • 2016-08-14
  • 2015-09-02
  • 2011-11-16
  • 2021-03-27
  • 2012-02-04
  • 2017-01-27
  • 2011-08-12
相关资源
最近更新 更多