这是一个奇怪的问题,我不知道该怎么做。
我有以下内容:
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 :: strings已经损坏(有些字母已经随机改变了,尽管它始终是每次运行都一样。)
发生了什么事?
答案 0 :(得分:2)
我想我可以解释你所遇到的“双重免费核心转储”问题。我最近遇到了同样的事情,听起来你做的和我做的一样。
根据你的描述,你说当你“单独运行它们”时它们工作正常,但是如果你“一起运行它们”你会得到双重免费/核心转储问题。
如果同一个全局声明两次,我发现会发生这种情况。
在我的情况下,我有一个类foo,在一个文件中我有一个全局class foo gFoo;
,在另一个文件中我有一个全局class foo gFoo;
。 (是的,这听起来很愚蠢,实际上我是链接文件X.cxx以及包含X.cxx的共享库 - 结果基本相同。)
现在,我原本期望编译器对此抱怨,但显然有标志可以启用或禁用此检查,并且代码编译正常。但是当程序终止并调用它的所有析构函数时,它会两次调用gFoo的析构函数并给我双重自由消息以及核心转储。
鉴于你声明它可以独立工作但在组合时失败,我打赌你在两个单独的文件中定义全局,并且当它们自己编译时工作正常,但当你将它们组合成一个单独的时候测试,你可能有两次全局声明。
检查出来。
答案 1 :(得分:0)
C和C ++中的静态变量不是线程安全的。这意味着如果两个线程试图访问你的单例对象,就会发生竞争条件(坏事)。解决问题的一种方法是使用线程本地存储。这是由pthreads库支持的,一些编译器也直接支持线程本地存储。
另一种方法是,如果你的单例必须是所有线程的全局,那就是提供锁定以确保一次只有一个线程可以访问你的数据。
然而,从那时起,问题才出现在单元测试中。我建议不要运行多线程单元测试,除非你打算在多线程中使用你的单例。
答案 2 :(得分:-1)
这高度依赖于您正在使用的编译器和平台,并且实际上没有看到测试我只能猜测发生了什么。
我在你的代码中看到了一些误解:
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(); }
单例模式适用于在整个程序运行中只有一个实例的类,而这似乎不是您的类。
通过这种方式,您可以安全地使用多个线程中的getParms()
函数,智能编译器将优化隐含的附加副本。