C++ 叮当声;gcc don';使用智能指针时,是否警告多态性的非虚拟基析构函数?

C++ 叮当声;gcc don';使用智能指针时,是否警告多态性的非虚拟基析构函数?,c++,c++11,smart-pointers,unique-ptr,virtual-destructor,C++,C++11,Smart Pointers,Unique Ptr,Virtual Destructor,我们知道,如果存在虚函数,那么基类析构函数也应标记为虚函数,否则,当使用基类指针显式地删除时,这是未定义的行为如果我们希望使用基类指针删除派生对象,则基类析构函数应标记为虚函数,否则它是未定义的行为 比如说, struct Base { virtual void greet() { std::cout << "base\n"; } }; struct Derived : public Base { virtual void greet() override { std::c

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

比如说,

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

struct Derived : public Base {
  virtual void greet() override { std::cout << "derived\n"; }
};
-Wdelete非虚拟数据或时,clang(gcc类似)将发出此类警告:

delete called on 'Base' that has virtual functions but non-virtual destructor
但是它们都不报告智能指针警告:

std::unique_ptr sb=std::make_unique();
//std::unique_ptr sb=std::unique_ptr(新派生);
某人->问候;

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

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


如果你通过,你会看到警告。不幸的是,它隐藏在一堆其他警告中。

其他答案尚未提及:

此问题仅存在于
唯一的\u ptr
,而不存在于
共享的\u ptr

这两个智能指针都可以有自定义的删除程序;但是
unique_ptr
默认删除基本指针,而
shared_ptr
默认删除派生指针(如果使用
make_shared
或等效项)

解决此问题的另一种方法是为删除派生指针的
unique\u ptr
提供自己的自定义删除程序。对于希望避免引入vtable的开销的情况,这可能是一个很好的解决方案


进一步阅读:,

关于clang(大约半年后将在clang 6.0中发布),clang的
-Wdelete非虚拟dtor
警告将对此发出警告。

好吧,你的第一句话概括了几个不相关的东西。规则是,如果析构函数是非虚拟的,则通过基类指针删除派生类是UB。就这些。该类是否具有任何其他虚拟函数完全无关。其他虚拟函数的存在只是一个松散的经验法则标准,这表明该类相对而言有可能通过基类指针被删除。@AndreyT感谢您指出,为了避免误解,我更新了原始问题。但是我刚刚发现,如果没有调用
虚拟函数
,gcc/clang不会发出警告,为什么?太多的诊断,我猜普通程序员永远不会使用
-Wsystem头
;从这个意义上讲,我们自己必须更加小心:-(@HongxuChen是的,发现这一点我真的很失望;让我吃惊的是,
unique\u ptr
在任何方面都不如原始指针安全。幸运的是,GCC和Clang都有
-Wnon虚拟dtor
,它警告非虚拟析构函数,即使你不尝试
删除
指针。
delete called on 'Base' that has virtual functions but non-virtual destructor
std::unique_ptr<Base> sb = std::make_unique<Derived>();
//   std::unique_ptr<Base> sb = std::unique_ptr<Derived>(new Derived);
sb->greet();