首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用CxxTest损坏的单例数据

使用CxxTest损坏的单例数据
EN

Stack Overflow用户
提问于 2010-09-01 00:38:30
回答 3查看 640关注 0票数 1

这是一个奇怪的问题,我不知道该怎么理解它。

我有一些类似以下的东西:

代码语言:javascript
复制
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;
    }
};

我通常是这样使用的:

代码语言:javascript
复制
Parms myParms = Parms::getDefaults();
myParms.parm1 = "crap";
functionThatNeedsParms(myParms);

非常简单。这从来没有给我带来任何麻烦,直到我开始尝试使用CxxTest编写使用这些代码的单元测试。我在不同的文件中有两个测试套件类,当我单独运行它们时,一切都很好。

当我把它们放在一起运行时,我看到了两个不好的东西。首先,整个核心转储试图双倍释放静态缺省变量。其次,如果我在defaults终止前一段时间查看它的内容,但在我开始使用它之后,其中的静态const std::字符串被损坏(一些字母随机更改,尽管每次运行时都是相同的)。

怎么一回事?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-09-02 02:04:34

双重释放和核心转储

我想我可以解释你遇到的“双重释放和核心转储”问题。我最近遇到了同样的事情,听起来你也在做和我一样的事情。

从你的描述中,你说当你“单独运行它们”时,它们工作得很好,但如果你“一起运行它们”,你就会得到双重空闲/核心转储问题。

我发现,如果同一个全局变量被声明两次,就会发生这种情况。

在我的例子中,我有一个类foo,在一个文件中有一个全局class foo gFoo;,在另一个文件中有一个全局class foo gFoo;。(是的,这听起来很愚蠢,实际上我链接的是一个文件X.cxx以及一个也包含X.cxx的共享库--结果基本上是一样的。)

现在,我已经预料到编译器会抱怨这一点,但显然有标志来启用或禁用此检查,并且代码编译良好。但是当程序终止并调用它的所有析构函数时,它调用了gFoo的析构函数两次,并给了我一个双重释放消息和一个核心转储。

既然你说它独立工作很好,但组合起来就失败了,我打赌你在两个单独的文件中定义了全局,当它们自己编译时,它工作得很好,但当你把它们组合在一起进行一个测试时,你可能会让全局声明发生两次。

看看这个。

票数 2
EN

Stack Overflow用户

发布于 2010-09-01 00:51:57

C和C++中的静态变量不是线程安全的。这意味着如果两个线程试图访问你的单例对象,就会发生竞争情况(坏事)。解决问题的一种方法是使用线程本地存储。这是由pthread库支持的,而且一些编译器直接支持线程本地存储。

如果您的单例必须对所有线程都是全局的,那么另一种方法是提供锁,以确保一次只有一个线程可以访问您的数据。

然而,这个问题只出现在单元测试中。我建议不要运行多线程单元测试,除非您打算在多线程中使用单例。

票数 0
EN

Stack Overflow用户

发布于 2010-09-01 01:02:39

这高度依赖于您正在使用的编译器和平台,并且在没有实际看到测试的情况下,我只能猜测发生了什么。

我在你的代码中发现了一些误解:

1)您缺少复制运算符和复制构造函数您正在复制包含std::string的实例,该实例可能使用引用计数来实现。引用计数是在std::string的重载复制构造函数/操作符中实现的,但它们可能不会从您的类的隐式生成的复制构造函数中调用,从而导致双重释放的内存和其他令人讨厌的事情。复制运算符/构造函数应如下所示:

代码语言:javascript
复制
// 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)我不太理解value1value2的存在。似乎你从来没有初始化过它们,你只是在默认的构造函数中使用它们来将它们(空的)内容复制到parm1parm2中。您可以完全避免这种情况,因为parm1parm2在调用时会自动初始化为空字符串。

3)这里不需要使用单例模式。getDefaults()方法可以按如下方式实现:

代码语言:javascript
复制
static Parms getParms() { return Parms(); }

单例模式适用于在整个程序运行过程中只有一个实例的类,而您的类似乎不是这种情况。

这样,您就可以安全地从多个线程使用getParms()函数,并且智能编译器将优化出隐含的额外副本。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3611182

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档