这是一个奇怪的问题,我不知道该怎么理解它。
我有一些类似以下的东西:
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编写使用这些代码的单元测试。我在不同的文件中有两个测试套件类,当我单独运行它们时,一切都很好。
当我把它们放在一起运行时,我看到了两个不好的东西。首先,整个核心转储试图双倍释放静态缺省变量。其次,如果我在defaults终止前一段时间查看它的内容,但在我开始使用它之后,其中的静态const std::字符串被损坏(一些字母随机更改,尽管每次运行时都是相同的)。
怎么一回事?
发布于 2010-09-02 02:04:34
双重释放和核心转储
我想我可以解释你遇到的“双重释放和核心转储”问题。我最近遇到了同样的事情,听起来你也在做和我一样的事情。
从你的描述中,你说当你“单独运行它们”时,它们工作得很好,但如果你“一起运行它们”,你就会得到双重空闲/核心转储问题。
我发现,如果同一个全局变量被声明两次,就会发生这种情况。
在我的例子中,我有一个类foo,在一个文件中有一个全局class foo gFoo;,在另一个文件中有一个全局class foo gFoo;。(是的,这听起来很愚蠢,实际上我链接的是一个文件X.cxx以及一个也包含X.cxx的共享库--结果基本上是一样的。)
现在,我已经预料到编译器会抱怨这一点,但显然有标志来启用或禁用此检查,并且代码编译良好。但是当程序终止并调用它的所有析构函数时,它调用了gFoo的析构函数两次,并给了我一个双重释放消息和一个核心转储。
既然你说它独立工作很好,但组合起来就失败了,我打赌你在两个单独的文件中定义了全局,当它们自己编译时,它工作得很好,但当你把它们组合在一起进行一个测试时,你可能会让全局声明发生两次。
看看这个。
发布于 2010-09-01 00:51:57
C和C++中的静态变量不是线程安全的。这意味着如果两个线程试图访问你的单例对象,就会发生竞争情况(坏事)。解决问题的一种方法是使用线程本地存储。这是由pthread库支持的,而且一些编译器直接支持线程本地存储。
如果您的单例必须对所有线程都是全局的,那么另一种方法是提供锁,以确保一次只有一个线程可以访问您的数据。
然而,这个问题只出现在单元测试中。我建议不要运行多线程单元测试,除非您打算在多线程中使用单例。
发布于 2010-09-01 01:02:39
这高度依赖于您正在使用的编译器和平台,并且在没有实际看到测试的情况下,我只能猜测发生了什么。
我在你的代码中发现了一些误解:
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()函数,并且智能编译器将优化出隐含的额外副本。
https://stackoverflow.com/questions/3611182
复制相似问题