c++ Meyers singleton undefined reference

只愿长相守 提交于 2020-01-07 04:38:07

问题


I have the following code that implements a basic Meyers singletone:

#ifndef _cConfigFile_HH
#define _cConfigFile_HH

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

#endif

My compiler doesn't allow me to compile this, giving the following error:

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

From the error I understand that I need to declare "instance" in a .cpp file, but am unable to declare cConfigFile::instance because the compiler says:

‘cConfigFile cConfigFile::instance’ is not a static

What am I doing wrong?? I'm lost here..


回答1:


You forgot to implement your constructor.




回答2:


The first error message means that you're missing the constructor for the instance.

As for the second: It would be a really good idea to include the .cpp code where you tried to define the instance. But it sounds like you tried to define the instance variable as a class static, which you're not supposed to do.

To instantiate the instance in a .cpp, you want to have something like this in your .hh file:

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

And this in your .cpp file:

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

    return instance;
}

NOTE: You really want to define the getInstance method in a .cpp file, not as an inline function. Defining it as inline will give you one instance of instance for every .cpp file in which the header is used. That defeats the purpose of trying to make it a singleton!




回答3:


You need to initialize the static instance at the bottom of the header file:

cConfigFile cConfigFile::instance;

And you need to take the declaration of the static instance outside of the getInstance() function.

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

  cConfigFile & getInstance(){

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

cConfigFile cConfigFile::instance();  //initialize



回答4:


This isn't exactly an answer, just a response to a comment that's too long for another comment, and it is relevant to this question.

Please don't use Singleton. Your stated reason for doing so is that you will be using it to store configuration information, and that configuration information needs to be accessible from anywhere in the program.

This is a reason (though, IMHO, not necessarily a great one) for a global variable, but not a reason for a Singleton. What harm is there in more than one object that stores the configuration existing? If all users used a global variable to access it, they would all use the same one. Why is it helpful to forcibly constrain the number of instances to one?

Secondly, what happens in the future when you need to temporarily add some configuration options? Say somehow your program needs to sort of run itself as a sub-program or something with slightly different configuration information, then restore the original configuration? Or maybe you need to frob the configuration in a few different ways for tests and then restore it to its original state? With a Singleton and a global variable this becomes very tricky. But if you used something more flexible, it's pretty trivial.

Now personally, I think passing around the configuration options to all the things that need them in an explicit fashion is not necessarily a bad way to go. But if you think that's intolerable, here's an alternative:

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

This allows you to replace the current instance for a scope and have it automatically restored when that scope goes away. And it also allows you to say DynamicallyScoped<Foo>::curInstance()->get_something(); anywhere in your program, except in the constructors for static or global objects, and expect to get something useful.

It's a doodle, and probably useful in this form. But I can imagine ways in which it might be better. For example, with some modification you could have several dynamically scoped variables of the same type.

Example usage:

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

As for the worry that more instances of your 'global config file' object could be created, don't hardcode the filename. Construct the object in main as a stack variable and give it the filename as an argument. Then there's no problem if other parts of the code create instances of the config file object unless they also give the name of the global configuration file. And if they do that, they deserve what they get.



来源:https://stackoverflow.com/questions/4808416/c-meyers-singleton-undefined-reference

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!