2014-06-11 111 views
1

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

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

我有两套的PC代码,我相信应该工作,我目前的解决方法代码...

非工作代码:

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

工作(但bodged)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

我不知道这是否会对您的问题有任何影响,但单建设者应该声明'private' – bstar55

+0

是不是第二种方式(工作方式)单一实例应该的方式被创建?不工作的方式每次调用时会创建一个新的“Pc”。 – gta0004

+0

@ bstar55是的,他们应该尝试尽一切努力来实现这个目标,而这一切都是在摆弄代码时遗留下来的,遗憾的是代码不工作。 – Mattiemus

回答

0

好的!头部搔抓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*) 
{ 
} 
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 

UPDATE:
但我在这里看到一个普遍的问题,使您的整个设计有点怀疑:

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函数的名称时,它们具有标准中定义的语义。为了使其充当独立环境的独立实施,请使用“免费”选项;它会将STDC_HOSTED定义为0,并且不会对标准库中函数名的含义做出假设,但有以下例外情况。要构建一个操作系统内核,您可能仍然需要为连接和启动做出自己的安排。见Options Controlling C Dialect

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

+0

机器实现许多不同体系结构(Pc/Mac等)的方法,以便基类实现全局可用函数,其中派生类实现更多变量类型以及实现一些虚函数。 此代码仍然无法正常工作 - Pc构造函数像以前一样被多次调用,使用静态局部变量的要求是什么?我正在编译 - 这是否会删除对它们的支持? – Mattiemus

+0

@Mattiemus _'Machine实现了许多不同架构的方法__这仍然可以使用这种模式完成。 _'和一些虚拟函数的实现。'根据我的建议,你根本不需要虚拟函数(请参阅'self'的'static_cast <>')。我想你一次只需要一个架构吧?我实际上找不到那个“-freestanding”编译器选项的文档(你有吗?),但我怀疑它确实应该影响本地静态初始化的行为。这是从当前C++标准定义的。 –

+0

@πάνταῥεῖ,我也用单态的CRTP模式。迄今为止运作良好。顺便说一句,你错过了函数中的return语句。 –