我有一个虚拟单例,我想实现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项目的)
答案 0 :(得分:3)
...让我相信静态变量已经再次初始化。
对于允许公开构建(或复制)Machine
和Pc
个实例的术语,这可能是一种正确的看法。
嗯,你的代码在这里
Pc* Pc::getInstance() {
static Pc* pc = new Pc();
return pc;
}
和方法的签名
static Machine* getInstance();
最好是
Pc& Pc::getInstance() {
static Pc theInstance;
return theInstance;
}
static Machine& getInstance();
还为类Machine
和Pc
私有的默认,复制构造函数,赋值运算符。
因此,实际上只能使用引用变量来访问单例实例
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 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*)
{
}