我在Windows 10上使用MySQL Connector / C ++。我观察到,当我从全局对象的对象调用get_driver_instance
时,它只会崩溃并引发异常:访问冲突读取位置。
示例代码:
#define CPPCONN_LIB_BUILD // Need to include this as we are linking mysql connector in static library
#include "MySQL\Src\mysql-connector-c++-1.1.5\cppconn\driver.h"
#pragma comment(lib,"MySQL\\Src\\mysql-connector-c++-1.1.5\\BLD\\driver\\Debug\\mysqlcppconn-static.lib")
#pragma comment(lib,"MySQL\\Src\\mysql-5.6.24\\BLD\\libmysql\\Debug\\mysqlclient.lib")
struct someclass
{
sql::Driver *m_pDriver;
someclass()
{
/* Create a connection */
m_pDriver= get_driver_instance(); // It crashes here
cout << "print something";
}
} someclass_instance;
int main(int argc, char** argv)
{
// We don't need to do anything here. The problem occurs in the global class constructor which executes before main as we've defined its global instance.
return 0;
}
抛出的异常与此类似:
Sample.exe中0x000007F6BE17485D的未处理异常:0xC0000005: 访问冲突读取位置0x0000000000000008。
崩溃时的Stacktrace:
Sample.exe!std::_Tree<class std::_Tmap_traits<class sql::SQLString,class boost::shared_ptr<class sql::mysql::MySQL_Driver>,struct std::less<class sql::SQLString>,class std::allocator<struct std::pair<class sql::SQLString const ,class boost::shared_ptr<class sql::mysql::MySQL_Driver> > >,0> >::_Lbound(class sql::SQLString const &) Unknown
Sample.exe!std::_Tree<class std::_Tmap_traits<class sql::SQLString,class boost::shared_ptr<class sql::mysql::MySQL_Driver>,struct std::less<class sql::SQLString>,class std::allocator<struct std::pair<class sql::SQLString const ,class boost::shared_ptr<class sql::mysql::MySQL_Driver> > >,0> >::lower_bound(class sql::SQLString const &) Unknown
Sample.exe!std::_Tree<class std::_Tmap_traits<class sql::SQLString,class boost::shared_ptr<class sql::mysql::MySQL_Driver>,struct std::less<class sql::SQLString>,class std::allocator<struct std::pair<class sql::SQLString const ,class boost::shared_ptr<class sql::mysql::MySQL_Driver> > >,0> >::find(class sql::SQLString const &) Unknown
Sample.exe!sql::mysql::get_driver_instance_by_name(char const * const) Unknown
Sample.exe!sql::mysql::get_driver_instance(void) Unknown
Sample.exe!get_driver_instance() Unknown
Sample.exe!someclass::someclass() Line 72 C++
Sample.exe!`dynamic initializer for 'obj''() Line 76 C++
Sample.exe!_initterm(void (void) * * pfbegin, void (void) * * pfend) Line 894 C
Sample.exe!_cinit(int initFloatingPrecision) Line 303 C
Sample.exe!__tmainCRTStartup() Line 227 C
Sample.exe!mainCRTStartup() Line 164 C
令人惊讶的是,这仅在调试版本中发生。但是,情况与this问题中的情况不同。 我已经确定要链接调试内置的连接器库。另外,如果我实例化相同的对象someclass_instance
,但是在main函数内部它不会在那里崩溃。这真的很奇怪。我怀疑与CRT初始化有关,但是不确定。
注意:我已经花了数小时来解决这个问题。这是真正的问题。拒绝投票之前,请至少尝试一次代码。
如果有人可以扔光,那就太好了。
答案 0 :(得分:1)
您不能依赖于静态初始化不同类的顺序(例如,在您的情况下,调试版本和发行版之间的顺序是不同的。)
这就是所谓的static initialization order ‘fiasco’。
您必须避免在本身依赖于静态初始化的静态初始化程序中调用任何代码。
MySQL驱动程序具有各种静态初始化程序,大概是导致您出现问题的一个是map of names to drivers。
避免这种情况的一种方法是使用功能级别为static的延迟构造的单例,该函数级别在首次调用该函数时进行初始化:
struct someclass
{
sql::Driver *m_pDriver;
someclass()
{
/* Create a connection */
m_pDriver= get_driver_instance(); // It crashes here
cout << "print something";
}
void foo() {}
};
someclass& someclass_instance()
{
static someclass instance;
return instance;
}
int main(int argc, char** argv)
{
someclass_instance().foo();
return 0;
}
请注意,如果您不使用最新的编译器(例如Visual Studio 2013或更早版本),someclass_instance()
可能不是线程安全的,如果是这种情况,则需要确保从在启动使用该功能的其他线程之前先使用单线程。
您还可以通过在get_driver_instance_by_name
内移动驱动程序映射来修复MySQL代码(或提交错误报告,要求他们为您完成此操作)。