std::shared_ptr 可调用父类的非虚析构函数
1 struct IBase 2 { 3 IBase() { 4 std::cout << "IBase::IBase()" << std::endl; 5 } 6 virtual ~IBase() { 7 std::cout << "IBase::~IBase()" << std::endl; 8 } 9 virtual void execute() = 0; 10 }; 11 using IBaseSP = std::shared_ptr<IBase>; 12 using IBaseUP = std::unique_ptr<IBase>; 13 14 class Foo : public IBase 15 { 16 public: 17 Foo() { 18 std::cout << "Foo::Foo()" << std::endl; 19 } 20 ~Foo() override { 21 std::cout << "Foo::~Foo()" << std::endl; 22 } 23 void execute() override { 24 std::cout << "Foo::execute()" << std::endl; 25 } 26 }; 27 28 class Bar : public IBase 29 { 30 public: 31 Bar() { 32 std::cout << "Bar::Bar()" << std::endl; 33 } 34 ~Bar() override { 35 std::cout << "Bar::~Bar()" << std::endl; 36 } 37 void execute() override { 38 std::cout << "Bar::execute()" << std::endl; 39 } 40 };
1 int main() { 2 IBase* p = new Foo(); 3 p->execute(); 4 delete p; 5 6 std::cout << "----------" << std::endl; 7 8 std::queue<IBaseUP> queueUP; 9 { 10 queueUP.push(std::make_unique<Foo>()); 11 queueUP.push(std::make_unique<Bar>()); 12 } 13 14 while (!queueUP.empty()) { 15 auto &p = queueUP.front(); 16 p->execute(); 17 queueUP.pop(); 18 } 19 20 std::cout << "----------" << std::endl; 21 22 std::queue<IBaseSP> queueSP; 23 { 24 queueSP.push(std::make_shared<Foo>()); 25 queueSP.push(std::make_shared<Bar>()); 26 } 27 28 while (!queueSP.empty()) { 29 auto &p = queueSP.front(); 30 p->execute(); 31 queueSP.pop(); 32 } 33 34 return 0; 35 }
本测试环境为VS2015。支持C++11标准。
一般的我们通过指针或引用在C++中实现多态。如果要把数据存入容器,则可以使用智能指针。
如果要确保对象可以正常析构,则需要将父类的析构函数声明为虚函数。这样通过父类指针delete时,可确保子类对象的析构函数被正常调用。
正常情况下的执行结果如下图所示,父类及子类的析构函数都可以被调用:
如果父类的析构函数不是虚函数,则结果如下图:
可以看到,只有父类的析构函数被调用了。子类的析构函数并没有被调用,则产生错误。
这一点上,对于原始的指针或者std::unique_ptr。其表现是一致的。也符合C++基本的语法。
但是对于std::shared_ptr。即便父类析构函数并非虚函数,其子类的析构函数依然可以被调用。
对于此种行为的原因尚未查明(待更新)。也许这是std::shared_ptr专门设计的一种特性。但为谨慎起见,我们也未必需要此特性。
C++11中引入了新的关键字“override”。对于子类中要实现多态(即要对父类方法进行override)的方法。在函数定义中使用override关键子,则编译器会帮助检测语法错误。如果父类方法未声明virtual则报错,提升代码质量。
如果使用了override关键字,则可不必在函数开头声明virtual。