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