C++ Майерс синглтон неопределенная ссылка

У меня есть следующий код, который реализует основной синглетон Мейерса:

#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, но я не могу объявить cConfigFile::instance, потому что компилятор говорит:

"cConfigFile cConfigFile::instance" не является статическим

Что я делаю неправильно?? Я потерялся здесь..

4 ответа

Вы забыли реализовать свой конструктор.

Первое сообщение об ошибке означает, что вам не хватает конструктора для экземпляра.

Что касается второго: было бы неплохо включить код.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;
}

ПРИМЕЧАНИЕ. Вы действительно хотите определить getInstance Метод в файле.cpp, а не как встроенная функция. Определение его как встроенного даст вам один экземпляр instance для каждого файла.cpp, в котором используется заголовок. Это побеждает цель попытаться сделать это единственным!

Вам нужно инициализировать статический экземпляр внизу файла заголовка:

cConfigFile cConfigFile:: instance;

И вам нужно взять объявление статического экземпляра вне функции getInstance().

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

  cConfigFile & getInstance(){

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

cConfigFile cConfigFile::instance();  //initialize

Это не совсем ответ, просто ответ на комментарий, который слишком длинный для другого комментария, и он имеет отношение к этому вопросу.

Пожалуйста, не используйте синглтон. Ваша заявленная причина для этого заключается в том, что вы будете использовать ее для хранения информации о конфигурации, и эта информация о конфигурации должна быть доступна из любой точки программы.

Это причина (хотя, IMHO, не обязательно отличная) для глобальной переменной, но не причина для 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 в качестве переменной стека и дать ему имя файла в качестве аргумента. Тогда не будет проблем, если другие части кода создадут экземпляры объекта файла конфигурации, если они также не дадут имя файлу глобальной конфигурации. И если они это делают, они заслуживают того, что получают.

Другие вопросы по тегам