2
#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:Database object destroy // 为什么这个被称为析构函数在数据库中不是虚拟的

数据库对象被破坏

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

4

3 回答 3

5

const当涉及对临时对象的引用时,这是一种特殊情况。正确调用了临时的析构函数,而不是引用的析构函数,因为毕竟临时的生命周期只是延长了。


类似于 Andrei Alexandrescu 在他的范围守卫中使用的技巧。不过,他使用了const对临时的引用。

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

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

通用:改变你编写异常安全代码的方式——永远


还解决了为什么在对基类的 const 引用上调用派生类的析构函数?

于 2013-01-23T02:47:35.333 回答
3

您正在将临时绑定到引用。通常这是不允许的,但 MSVC 有一个邪恶的扩展允许它。您可以通过声明const Database &ref = SqlServer();和注释您的ref.Open()行来在其他编译器中重现这一点,因为临时对象可能受 const 引用的约束。

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

于 2013-01-23T02:47:56.070 回答
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. 如果通过指向基的指针删除派生类型的对象,则会得到未定义的行为。

于 2013-01-23T03:47:29.787 回答