为什么派生类析构函数被调用,即使基类析构函数不是虚拟的,如果将对象创建为引用

时间:2013-01-23 02:42:50

标签: c++ visual-c++ reference virtual

#include <iostream>
class Database
{
public:
   Database()
   {
      std::clog << "Database object created " <<std::endl ;
   }
   ~Database()
   {
      std::clog << "Database object destroyed " << std::endl;
   }
   virtual void Open(const std::string & ) = 0 ; 
} ;

class SqlServer : public Database
{
public:
   void Open(const std::string & conn)
   {
       std::clog << "Attempting to open the connection "<< std::endl ;
   }

   ~SqlServer()
   {
        std::clog << "SqlServer:Database object destroyed "<< std::endl ;
   }
} ;
int main()
{
   Database &ref = SqlServer();
   ref.Open("uid=user;pwd=default");
   return 0 ;
}

输出

  

创建数据库对象

     

试图打开连接

     

SqlServer:数据库对象被破坏//为什么这个被调用为析构函数在数据库中不是虚拟的

     

数据库对象已销毁

注意:如果我用pref替换ref,那么一切正常,即sqlserver析构函数不会被调用。

3 个答案:

答案 0 :(得分:5)

const涉及临时工具时,这是一种特殊情况。正确调用临时的析构函数,而不是引用的析构函数,因为毕竟临时的生命周期只是被扩展。


类似于Andrei Alexandrescu在他的守门员中使用的技巧。他使用const引用了一个临时的。

  

根据C ++标准,使用临时值初始化的引用使该临时值在引用本身的生命周期内存活。

     

临时变量与引用一样长 - 当它被销毁时,会调用正确的析构函数

来自Generic: Change the Way You Write Exception-Safe Code — Forever


还在Why is the derived class's destructor invoked on a const reference to the base class?

中处理

答案 1 :(得分:3)

您将临时绑定到引用。通常这是不允许的,但MSVC有一个允许它的邪恶扩展。您可以在其他编译器中重现这一点,方法是声明const Database &ref = SqlServer();并注释掉ref.Open()行,因为临时值可能会被const引用绑定。

因此,使用MSVC中的原始代码或其他编译器中的修改代码,您看到的析构函数消息来自临时销毁。引用使临时值保持活动状态,当引用超出范围时,临时值也是如此。

答案 2 :(得分:0)

Database &ref = SqlServer();

ref绑定到临时引用,你可以使用VS扩展来绑定到const引用,但是更好的是不使用它,那些扩展是邪恶的。建议使用智能指针。

class Database
{
public:
   Database()
   {
      std::clog << "Database object created " <<std::endl ;
   }
   ~Database()
   {
      std::clog << "Database object destroyed " << std::endl;
   }
   virtual void Open(const std::string & ) = 0 ; 
   virtual ~Database() {}
} ;


std::unique_ptr<Database> conn(new SqlServer());
conn->Open("uid=user;pwd=default");

注意:您的Database类充当基类,但尚未定义virtual destructor。 如果通过指针删除派生类型的对象,则会得到未定义的行为 基地。