静态局部变量被重新初始化

Static local variable gets reinitialized

本文关键字:初始化 局部变量 静态      更新时间:2023-10-16

我有一个虚拟的单例,我想实现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;
};

Bridge from Machine -> 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项目)

…让我相信静态变量又被初始化了

如果允许公开构建(或复制)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类的默认、复制构造函数和赋值操作符为private。因此,实际上只能使用引用变量来访问单例实例

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;
}

查看完整的工作示例。


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

首先,它是一个c编译器选项(不应该影响您的c++代码),其次,正如我在GCC文档

中发现的那样

GCC的目标是作为一个符合标准的独立实现可用,或者作为一个符合标准的托管实现的编译器。默认情况下,它将充当宿主实现的编译器,将STDC_HOSTED定义为1,并假设在使用ISO C函数的名称时,它们具有标准中定义的语义。要使其作为独立环境的符合标准的独立实现,请使用选项- freestanding;然后它将STDC_HOSTED定义为0,并且不假设来自标准库的函数名的含义,例外情况如下所示。要构建OS内核,您可能仍然需要自己安排链接和启动。

这并没有给我任何关于局部静态初始化的未定义行为的点

Ok!经过3天的挠头,我找到了错误的原因!如果你使用--ffreestanding来编译你的代码,GCC希望你为自己提供一些函数,以防其他人有这个(授予非常模糊的)错误,我把我使用的代码放在下面(非常感谢osdev.org的这段代码!!)

__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*)
{
}