【发布时间】:2020-10-29 01:23:10
【问题描述】:
有一个虚类作为回调接口,我不能修改,也不能要求作者修复。该类的唯一成员是很多可以覆盖的虚拟方法,以便让库回调到我的代码中。为了获得一些回调机会,我应该创建该虚拟类的派生类,并覆盖相应的虚拟方法。如果我对某些回调机会不感兴趣,我只需要避免覆盖它们。
但是,该接口类的声明存在缺陷 - 其析构函数未声明为虚拟。
例如:
class callback_t {
public:
virtual void onData( int ) {};
};
我创建了一个子类并且不重写析构函数,但是当我删除类child_t的动态对象时,我遇到了来自编译器的警告(gcc9 with C++17):
删除具有非虚拟析构函数的多态类类型“child_t”的对象可能会导致未定义的行为。
class child_t : public callback_t {
public:
~child_t() {
// release things specific to the child...
};
void onData( int ) override {
// do things I want when onData
};
private:
int m_data = 0;
};
int main() {
child_t* pc = new child_t;
// pass pc into the routines of the library
// working...
delete pc; /*deleting object of polymorphic class type ‘child_t’ which has non-virtual destructor might cause undefined behavior */
};
问题: 如何正确优雅地消除警告(我必须提交没有警告的代码)?
注释和修改:
-
我不能修改类callback_t的声明,我也不能要求它的作者修复!这是一个由权威机构发布的库。 劝我改基类也没用,判断lib的代码质量也没意义;
-
我从来没有打算用基类类型的指针释放child_t类的对象,我清楚地知道virtual-dtor和static-dtor的区别;
-
我需要解决的主要问题是消除编译警告,因为我确定没有内存泄漏,并且没有遗漏恢复某些状态;
-
在这种情况下,基类中没有数据,没有有意义的代码,而制作它的唯一目的是派生自,因此不应将其标记为 final。但我尝试将 child_t 设为“
final”,警告消失了。我不确定这种方法是否正确。如果是这样,我认为这是迄今为止最便宜的方法; -
我还尝试将 child_t 的 dtor 设为
virtual,警告也消失了。但我仍然不确定它是否 是正确的。
【问题讨论】:
-
我可以想到两件事:仅使用堆栈变量和智能指针,因此不需要显式删除内存,如果在析构函数中不需要更改任何内容,那么您就可以开始了。如果不是这种情况,那么是否存在许多虚拟方法中的任何一个,您知道您不需要某些东西或可以设置一些原本会在析构函数中完成的状态?因为那时你可以在那里做,而不是析构函数。
-
@Andrew“如果在析构函数中不需要更改任何内容”,这不是决定是否存在问题的正确依据。要么你遇到未定义的行为,要么你没有。在这两种情况下,析构函数的实际作用并不重要
-
如果您要通过该基类指针删除对象,则只需要一个虚拟析构函数。对于接口类,这并不常见(尽管有时会这样做)。礼貌起见,应该将接口类的析构函数设为
protected:以明确这一点。 -
你为什么要这样做:
child_t* pc = new child_t;?你不能使用child_t child;并将&child传递给“库的例程”吗?
标签: c++ polymorphism overriding derived-class virtual-destructor