C++ 使用CxxTest损坏的单例数据
这是一个奇怪的问题,我不知道该怎么办 我有如下几点:C++ 使用CxxTest损坏的单例数据,c++,static,singleton,coredump,C++,Static,Singleton,Coredump,这是一个奇怪的问题,我不知道该怎么办 我有如下几点: struct Parms { const std::string value1; const std::string value2; std::string parm1; std::string parm2; Parms() : parm1(value1), parm2(value1) {} static const Parms& getDefaults() {
struct Parms
{
const std::string value1;
const std::string value2;
std::string parm1;
std::string parm2;
Parms() : parm1(value1), parm2(value1) {}
static const Parms& getDefaults()
{
static Parms defaults;
return defaults;
}
};
我通常这样使用:
Parms myParms = Parms::getDefaults();
myParms.parm1 = "crap";
functionThatNeedsParms(myParms);
很简单。这从来没有给我带来任何麻烦,直到我开始尝试使用CxxTest编写使用这段代码的单元测试。我在不同的文件中有两个测试套件类,当我单独运行它们时,一切都很好
当我把它们放在一起时,我看到了两件坏事。首先,整个内核转储都试图双重释放静态默认值变量。其次,如果我在默认值消失之前的某个时间查看它的内容,但在我开始使用它之后,其中的静态const std::string已损坏(有些字母已随机更改,但每次运行时都是相同的)
<> P> > C和C++中的静态变量不是线程安全的。这意味着,如果两个线程试图访问您的singleton对象,可能会发生争用条件(坏事情)。解决问题的一种方法是使用线程本地存储。pthreads库支持这一点,一些编译器也直接支持线程本地存储 如果您的单线程必须对所有线程都是全局的,那么另一种选择是提供锁,以确保一次只有一个线程可以访问您的数据
然而,问题只出现在单元测试中。我建议不要运行多线程单元测试,除非您打算在多个线程中使用单线程。 < P> C和C++中的静态变量不是线程安全的。这意味着,如果两个线程试图访问您的singleton对象,可能会发生争用条件(坏事情)。解决问题的一种方法是使用线程本地存储。pthreads库支持这一点,一些编译器也直接支持线程本地存储 如果您的单线程必须对所有线程都是全局的,那么另一种选择是提供锁,以确保一次只有一个线程可以访问您的数据
然而,问题只出现在单元测试中。我建议不要运行多线程单元测试,除非您打算在多线程中使用单线程。这在很大程度上取决于您正在使用的编译器和平台,在没有实际看到测试的情况下,我只能猜测发生了什么。
我在您的代码中看到一些误解:
1) 缺少复制运算符和复制构造函数 您正在复制包含
std::string
的实例,该实例可能使用引用计数实现。引用计数是在std::string
的重载复制构造函数/运算符中实现的,但是这些可能不会从类的隐式生成的复制构造函数中调用,因此会导致双重释放内存和其他讨厌的事情。复制运算符/构造函数应如下所示:
// Copy constructor
Parms(const Parms& oth) { parm1 = oth.parm1; parm2 = oth.parm2; }
// Copy operator
Parms& operator= (const Parms& oth) {
if (&oth == this) // Check for self-assignment
return *this;
parm1 = oth.parm1;
parm2 = oth.parm2;
return *this;
}
2) 我不太理解
value1
和value2
的存在。似乎您从未初始化它们,您只是在默认构造函数中使用它们将其(空)内容复制到parm1
和parm2
中。您可以完全避免这种情况,因为调用时,parm1
和parm2
会自动初始化为空字符串。
3) 这里不需要使用单例模式。
getDefaults()
方法可以实现如下:
static Parms getParms() { return Parms(); }
singleton模式适用于在整个程序运行过程中只有一个实例的类,而您的类似乎不是这样
通过这种方式,您可以从多个线程安全地使用
getParms()
函数,智能编译器将优化出隐含的附加副本。这在很大程度上取决于您使用的编译器和平台,在没有实际看到测试的情况下,我只能猜测正在进行的操作。我在您的代码中看到一些误解:
1) 缺少复制运算符和复制构造函数 您正在复制包含
std::string
的实例,该实例可能使用引用计数实现。引用计数是在std::string
的重载复制构造函数/运算符中实现的,但是这些可能不会从类的隐式生成的复制构造函数中调用,因此会导致双重释放内存和其他讨厌的事情。复制运算符/构造函数应如下所示:
// Copy constructor
Parms(const Parms& oth) { parm1 = oth.parm1; parm2 = oth.parm2; }
// Copy operator
Parms& operator= (const Parms& oth) {
if (&oth == this) // Check for self-assignment
return *this;
parm1 = oth.parm1;
parm2 = oth.parm2;
return *this;
}
2) 我不太理解
value1
和value2
的存在。似乎您从未初始化它们,您只是在默认构造函数中使用它们将其(空)内容复制到parm1
和parm2
中。您可以完全避免这种情况,因为调用时,parm1
和parm2
会自动初始化为空字符串。
3) 这里不需要使用单例模式。
getDefaults()
方法可以实现如下:
static Parms getParms() { return Parms(); }
singleton模式适用于在整个程序运行过程中只有一个实例的类,而您的类似乎不是这样
通过这种方式,您可以从多个线程安全地使用getParms()
函数,并且智能编译器将优化隐含的额外副本。双重空闲和核心转储
我想我可以解释你所面临的“双重免费和核心转储”问题。我最近遇到了同样的事情,听起来你在做和我一样的事情
根据您的描述,您说过,当您“单独运行它们”时,它们工作正常,但如果您“一起运行”,则会出现双自由/核心转储问题
我发现如果两次声明同一个全局变量,就会发生这种情况
在我的例子中,我有一个foo类,在一个文件中,我有一个全局类foo-gFoo在另一个文件中,我有一个全局类foo-gFoo代码>。(是的,这听起来很愚蠢,实际上我是在链接一个文件X.cxx a