#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析构函数不会被调用。
答案 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
。
如果通过指针删除派生类型的对象,则会得到未定义的行为
基地。