c ++ Meyers singleton undefined reference

时间:2011-01-26 18:30:03

标签: c++ singleton

我有以下代码实现了基本的Meyers单线:

#ifndef _cConfigFile_HH
#define _cConfigFile_HH

class cConfigFile {
public:
  static cConfigFile& getInstance() {
    static cConfigFile instance;
    return instance;
  };
private:
  cConfigFile();
};

#endif

我的编译器不允许我编译它,给出以下错误:

/include/cConfigFile.hh:7: undefined reference to `cConfigFile::cConfigFile()'

从错误我明白我需要在.cpp文件中声明“instance”,但是我无法声明cConfigFile :: instance,因为编译器说:

  

'cConfigFile cConfigFile :: instance'不是静态的

我做错了什么?我迷路了......

4 个答案:

答案 0 :(得分:10)

您忘了实施构造函数。

答案 1 :(得分:-1)

第一条错误消息表示您缺少实例的构造函数。

至于第二种:在您尝试定义实例的地方包含.cpp代码是一个非常好的主意。但听起来你试图将实例变量定义为静态类,你不应该这样做。

要在.cpp中实例化实例,您希望在.hh文件中包含类似的内容:

class cConfigFile {
public:
  static cConfigFile& getInstance();
private:
  cConfigFile() {} // Note: define the constructor -- this is probably not enough!
};

这在你的.cpp文件中:

cConfigFile& cConfigFile::getInstance()
{
    // Define the singleton as a local static
    static cConfigFile instance;

    return instance;
}

注意:您确实想要在.cpp文件中定义getInstance方法,而不是内联函数。将其定义为内联将为每个使用标头的.cpp文件提供一个instance实例。这就失败了试图让它变成单身人士的目的!

答案 2 :(得分:-2)

您需要初始化头文件底部的静态实例:

cConfigFile cConfigFile :: instance;

你需要在getInstance()函数之外接受静态实例的声明。

class cConfigFile{
public:
  static cConfigFile instance;   //declare

  cConfigFile & getInstance(){

    return instance;
  }
private:
  cConfigFile(){}
};

cConfigFile cConfigFile::instance();  //initialize

答案 3 :(得分:-2)

这不是一个答案,只是对另一条评论太长的评论的回复,而且与此问题相关。

请不要使用Singleton。您说明这样做的原因是您将使用它来存储配置信息,并且需要从程序中的任何位置访问配置信息。

这是一个全局变量的原因(尽管,恕我直言,不一定是伟大的),但不是单身人士的理由。存储配置的多个对象有什么危害?如果所有用户都使用全局变量来访问它,那么它们都将使用相同的变量。为什么将实例数量强制约束为一个有用?

其次,当您需要临时添加一些配置选项时,将来会发生什么?以某种方式说你的程序需要将自己作为一个子程序或配置信息略有不同的东西运行,然后恢复原始配置?或者您可能需要以几种不同的方式对配置进行测试,然后将其恢复到原始状态?使用Singleton和全局变量,这变得非常棘手。但如果你使用更灵活的东西,那就非常简单了。

就个人而言,我认为以明确的方式将配置选项传递给所有需要它们的东西并不一定是不好的方法。但如果你认为这是不可容忍的,那么这是另一种选择:

template <typename T>
class DynamicallyScoped {
 public:
   explicit DynamicallyScoped(T &stackinstance) : oldinstance_(0) {
      oldinstance_ = S_curinstance;
      S_curinstance = &stackinstance;
   }
   ~DynamicallyScoped() {
      S_curinstance = oldinstance_;
      oldinstance_ = 0;
   }
   static T *curInstance() { return S_curinstance; }

 private:
   static T *S_curinstance;
   T *oldinstance_;

   // Made private and left undefined on purpose.
   DynamicallyScoped(const DynamicallyScoped &b);
   const DynamicallyScoped &operator =(const DynamicallyScoped &b);
};

这允许您替换作用域的当前实例,并在该作用域消失时自动恢复。它还允许您在程序中的任何位置说DynamicallyScoped<Foo>::curInstance()->get_something();,除了静态或全局对象的构造函数,并希望得到一些有用的东西。

这是一个涂鸦,可能在这种形式下很有用。但我可以想象它可能会更好的方式。例如,通过一些修改,您可以拥有多个相同类型的动态范围变量。

使用示例:

#include <iostream>

template <>
int *DynamicallyScoped<int>::S_curinstance = 0;

extern void doSomething();
extern void doSomethingElse();
extern void printTheInt();

int main(int argc, char *argv[])
{
    int x = 5;
    DynamicallyScoped<int> theInt(x);

    printTheInt();
    doSomething();
    doSomethingElse();
}

void doSomething()
{
    printTheInt();
}

void doSomethingElse()
{
    int newint = 6;
    DynamicallyScoped<int> subint(newint);
    doSomething();
}

void printTheInt()
{
    ::std::cout << "_The_ integer's value is: "
                << *DynamicallyScoped<int>::curInstance() << '\n';
}

担心可能会创建更多“全局配置文件”对象的实例,请勿对文件名进行硬编码。将main中的对象构造为堆栈变量,并将其作为参数提供给它。然后,如果代码的其他部分创建配置文件对象的实例,则没有问题,除非它们还提供全局配置文件的名称。如果他们这样做,他们应该得到他们得到的东西。