静态局部变量被初始化

时间:2014-06-11 22:53:07

标签: c++ gcc c++11 singleton

我有一个虚拟单例,我想实现getInstance()静态函数,但是每次调用它时,静态对象都会被重新初始化,所以我每次都得到一个新实例,任何帮助都会不胜感激 - 这让我很困惑。

我要在以下方法中实现该方法:

class Pc : public Machine
{
  private:
    ... members ...

  public:
    static Pc* getInstance();

    Pc() {};
    virtual ~Pc() {}

    ... other functions ...
};

父类:

class Machine
{
  public:
    static Machine* getInstance();

    Machine() { }
    Machine(const Machine&) = delete;
    virtual ~Machine() { }

    ... methods ...

    void operator=(const Machine&) = delete;
};

从机器桥接 - > Pc singleton

Machine* Machine::getInstance()
{
    return Pc::getInstance();
}

我有两套用于PC的代码,一个我相信应该工作的代码,以及我当前的解决方法代码......

非工作代码:

Pc* Pc::getInstance()
{
    static Pc* pc = new Pc();
    return pc;
}

工作(但提交)getInstance()代码:

static Pc* pc = nullptr;
Pc* Pc::getInstance()
{
    if(pc == nullptr) {
        pc = new Pc();
    }
    return pc;
}

虽然两个编译成功,但在断开指向我的代码之后,我可以看到我的预期代码返回相同的指针,但是在操作对象之后,第二个调用返回一个新对象,导致我相信静态变量已经再次初始化。

用标志编译:

-ffreestanding -Wall -Wextra -fno-exceptions -fno-rtti -std=gnu++11 -lgcc

(这是针对OS项目的)

2 个答案:

答案 0 :(得分:3)

  

...让我相信静态变量已经再次初始化。

对于允许公开构建(或复制)MachinePc个实例的术语,这可能是一种正确的看法。

嗯,你的代码在这里

Pc* Pc::getInstance() {
    static Pc* pc = new Pc();
    return pc;
}

和方法的签名

static Machine* getInstance();

最好是

Pc& Pc::getInstance() {
    static Pc theInstance;
    return theInstance;
}

static Machine& getInstance();

还为类MachinePc私有的默认,复制构造函数,赋值运算符。 因此,实际上只能使用引用变量来访问单例实例

Machine& mach = Pc::getInstance();
Machine m2; // Fails to compile

<强>更新
但我在这里看到了一个普遍的问题,这使得整个设计有点可疑:

Machine& Machine::getInstance() {
    return Pc::getInstance();
}

这使得Machine类依赖于它的派生类,这使得基类完全没用。


使用模板类解决​​这个问题怎么样?

template<class Derived>
class Machine {
public:
     static Derived& getInstance() {
          static Derived theInstance;
          return theInstance;
     }
protected:
     Machine() {
         Derived* self = static_cast<Derived*>(this); // Ensure that Derived
                                                      // inherits from Machine
         (void)self; // suppress compiler warning for unused variable
     }
private:
     Machine(const Machine&);
     Machine& operator=(const Machine&);
};

class Pc : public Machine<Pc> {
    friend class Machine<Pc>;
    Pc() : Machine<Pc>() {}
};

int main() {
    Pc& pc = Pc::getInstance(); // Get a reference of the Pc singleton
    return 0;
}

查看fully working sample here


至于你提到的-ffreestanding编译器选项:

首先,它是一个c编译器选项(不应该影响你的c ++代码),第二,正如我在GCC documentation

中找到的那样
  

GCC旨在用作符合标准的独立实现,或用作符合标准的托管实现的编译器。默认情况下,它将充当托管实现的编译器,将 STDC_HOSTED 定义为1,并假设在使用ISO C函数的名称时,它们具有标准中定义的语义。为了使其成为独立环境的符合标准的独立实施,请使用选项-ffreestanding;然后,它会将 STDC_HOSTED 定义为0,并且不会对标准库中函数名称的含义进行假设,但下面会有例外情况。要构建操作系统内核,您可能仍需要自己安排链接和启动。请参阅Options Controlling C Dialect

这并没有给我任何关于本地静态初始化的未定义行为的观点。

答案 1 :(得分:0)

确定!头部刮伤3天后,我发现了错误的原因!如果您使用--ffreestanding编译代码,GCC希望您为自己提供一些函数,如果其他人有这个(授予非常模糊)错误,我已经把我在下面使用的代码(非常感谢{{ 3}}这个代码!!)

__extension__ typedef int __guard __attribute__((mode(__DI__)));

extern "C" int __cxa_guard_acquire(__guard* g)
{
    return !*(char*)(g);
}

extern "C" void __cxa_guard_release(__guard* g)
{
    *(char *)g = 1;
}

extern "C" void __cxa_guard_abort (__guard*)
{
}