高手进!C++作用域不同造成的智能指针困惑
tttk 2011-04-30 02:45:01 昨天在做项目测试时突然发现一个莫名异常。异常的现象很奇怪,表现在我的SQLite数据库更新时报死锁异常。
从数据库的死锁,你能想到什么?首先,虽然缺乏充分理由,我还是怀疑是更新数据格式的问题,可是测试到半夜,没发现任何问题。今天早上进一步思考:数据库的死锁要有死锁条件,google结果是,SQLite容易在多进程并发时死锁。多进程?哪里出来的多进程?
进一步检查数据库操作。我的数据库操作封装在一个singleton单例类中,所有的数据库操作都通过这个唯一接口进出。单例模式采用c++的auto_ptr实现。
static std::auto_ptr<CDBClass> m_hInstance;
static CDBClass* CDBClass::getInstance()
{
if (m_hInstance.get() == 0) {
m_hInstance = std::auto_ptr<CDBClass>(new CDBClass()); (1)
}
return m_hInstance.get();
}
数据库的连接在CDBClass的构造函数中实现。
CDBClass::CDBClass()
{
m_db.open();
……
}
在项目其它代码中通过以下代码引用:
CDBClass* dbclass = CDBClass::getInstance(); (2)
根据SQLite死锁条件的分析,出现死锁的最大可能是试图对已打开数据库再次执行连接操作。可是,数据库的操作是单例模式,数据库连接操作所在的构造函数只应该被执行一次才对。越想越糊涂,干脆把断点设在代码(1)处,开始跟踪。
结果,红色跟踪条第二次出现在断点(1)处。这个结果出乎我的意料,但又似乎合情合理。通过检查调用堆栈,两次断点依次出现在两个不同类A和B的构造函数中,两个构造函数都是通过代码(2)来获取对CDBClass的引用,从而间接的导致两次数据库连接。
进一步检查两个类A和B的构造过程,发现了问题:
A类声明为系统内部变量,在系统执行时,通过代码进行实例化,并;
B类声明为全局变量,在系统执行时,由系统负责实例化。
也就是说,两个类的构造函数通过调用CDBClass的getInstance()函数,本意是想获得全局唯一的单实例,可结果竟然获得的是两个完全不同的对象。原因只是因为它们在不同的作用域中被初始化。