clang&gcc在使用智能指针时不会警告非虚拟基础析构函数的多态性?

Hon*_*hen 10 c++ smart-pointers unique-ptr virtual-destructor c++11

我们知道如果有虚函数那么基类析构函数也应该被标记为虚拟,否则当显式地deleted使用基类指针时它是未定义的行为如果我们希望删除带有基类指针的派生对象,则基础析构函数应该被标记为虚拟,否则是未定义的行为.

例如,

struct Base {
  virtual void greet() { std::cout << "base\n"; }
};

struct Derived : public Base {
  virtual void greet() override { std::cout << "derived\n"; }
};
Run Code Online (Sandbox Code Playgroud)

呼叫

Base *b = new Derived;
b->greet();
delete (b);
Run Code Online (Sandbox Code Playgroud)

-wdelete-non-virtual-dtor时,clang(类似gcc)会发出这样的警告:

delete called on 'Base' that has virtual functions but non-virtual destructor
Run Code Online (Sandbox Code Playgroud)

但他们都没有报告智能指针的警告:

std::unique_ptr<Base> sb = std::make_unique<Derived>();
//   std::unique_ptr<Base> sb = std::unique_ptr<Derived>(new Derived);
sb->greet();
Run Code Online (Sandbox Code Playgroud)

我想这仍然导致未定义的行为,对吧?

T.C*_*.C. 13

是的,它仍然是未定义的行为.问题是delete调用发生在内部std::default_delete,这是在系统头内.默认情况下,编译器不会为系统标头中的代码生成警告.

如果你通过-Wsystem-headers,你会看到警告.不幸的是,它被埋在一堆其他警告中.


M.M*_*M.M 5

其他答案尚未提及:

这个问题只存在于unique_ptr,而不是为了shared_ptr.

这两个智能指针都可以有自定义删除器; 但unique_ptr默认为删除基指针,shared_ptr默认为删除派生指针(如果使用make_shared<Derived>或等效).

解决此问题的另一种方法是提供自己的自定义删除器unique_ptr,删除派生指针.对于您希望避免引入vtable的开销的情况,这可能是一个很好的解决方案.

进一步阅读:unique_ptr删除, shared_ptr删除