2010-05-04 2 views
0

Имеет ли значение, реализована ли реализация конструктора/деструктора в файле заголовка или в исходном файле? Например, какой путь является предпочтительным и почему?C++ Singleton Constructor and Destructor

Способ 1:

class Singleton 
{ 
    public: 
    ~Singleton() { } 

    private: 
    Singleton() { } 
}; 

Способ 2:

class Singleton 
{ 
    public: 
    ~Singleton(); 

    private: 
    Singleton(); 
}; 

В исходном файле .cc:

Singleton::Singleton() 
{ 
} 

Singleton::~Singleton() 
{ 
} 

Изначально у меня есть осуществление в исходном файле, но Меня попросили удалить его. Кто-нибудь знает, почему?

+6

Если это действительно одноэлементный, сделайте ctor и dtor частным. – Stephen

+0

Или просто не используйте их в первую очередь. – rlbond

+0

-1 Вопрос не имеет ничего общего с синглтонами, ни с конструкторами/деструкторами. Пожалуйста, измените название. –

ответ

2

Это не имеет значения, но, как правило, лучше (в моих скромных мнениях) определить их в .cpp-файле, чтобы скрыть реализацию от пользователей этого класса.

1

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

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

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

Что касается конструктора и деструктора singleton, они будут вызываться только один раз во всей программе, поэтому вы не получите ничего, вложив их в них, чтобы вы могли также вставлять их в исходный файл, если вы делая многое из них. Но если они пусты, зачем тратить пространство в исходном файле? Просто поместите их в файл заголовка.

+2

Если вы добавите функцию в заголовок, не помещая его в класс и не используя ключевое слово inline, вы получите неопределенные символы. –

+2

Компилятор может (теоретически) встроить методы, определенные в файле реализации, используя глобальную оптимизацию программ. –

+0

@John Совершенно верно. @ D.Shawley Я не знаю, какие компиляторы действительно это делают. Это противоречит тому, как обычно работает ссылка, так как вы не могли сделать такую ​​оптимизацию до тех пор, пока не произойдет соединение, и к этому моменту вся компиляция была выполнена, поэтому было бы слишком поздно встраивать что-либо. –

-1

Отличие состоит в том, что добавление реализации непосредственно в класс заставляет функцию быть встроенной.

+0

Нет, это не так. В C++ не предусмотрен метод принудительной вставки. Реализация бесплатна для встроенного, даже если метод определен в файле реализации ... предоставлен. Я не знаю того, что будет даже с глобальными оптимизациями. –

+0

Исправление _may вызывает функцию inline._ Что может быть или не быть желательно, особенно для конструкторов и деструкторов. – Stephen

+1

ya, но это будет иметь ограниченное использование. Поскольку конструктор будет вызываться только в 'getInstance', где он будет inlined –

0

Это имеет значение несколькими тонкими способами. функции, определенные в файле заголовка (ваш «путь 1») могут (в моем опыте, как правило,) быть объявлены встроенными (хотя, как и в inline, если функция велика, она не может быть встроена компилятором). Это может вызвать проблемы, особенно при работе с абстрактными классами.

Причина этого заключается в том, что vtable будет включен в блок компиляции (файл .o), где будет найдена первая не-встроенная функция. Если все функции встроены, v-таблица не будет найдена, и ваш код не будет связан. (Есть способы обойти это, но для другой темы.)

Итак, используйте способ 2 для более организованного кода, особенно для классов с множеством функций-членов и/или длинными функциями-членами, а также в случаях, когда у вас есть наследование и виртуальные функции, плавающие вокруг. Используйте путь 1 для очень коротких классов (где весь заголовочный файл будет меньше, чем около 100-200 строк).

+0

И когда я говорю« обычно », я имею в виду, что GCC, похоже, делает это со мной« все время » "(хотя могут быть обстоятельства, о которых я не знаю, когда это не так, я не разбираю все, что я компилирую, и я не читал источник GCC.) – SoapBox

0

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

Существует, однако, еще одна тонкость, которую мы можем исследовать здесь. Скажем, конструктор в классе Singleton является публичным:

class Singleton 
{ 
public: 
    Singleton(void); 
    virtual ~Singleton(void); 
}; 
//cpp file 
Singleton::Singleton() 
{ 
} 


Singleton::~Singleton() 
{ 
} 

Если поставить выше код в статическую библиотеку, что у вас есть, по существу, класс, который не может не быть реализован за пределами библиотеки. Это не будет компилятор, который помешает вам сделать это, но компоновщик. Это ошибка, вы получите:

1> testrun.obj: ошибка LNK2001: неразрешенный внешний символ "общественности: виртуальный __thiscall Singleton :: ~ Singleton (аннулируются)" (?? 1Singleton @@ UAE @ XZ)
1> testrun.obj: ошибка LNK2001: неразрешенный внешний символ "public: __thiscall Singleton :: Singleton (void)" (?? 0Singleton @@ QAE @ XZ)
1> C: \ temp \ sotest \ Debug \ testrun .exe: фатальная ошибка LNK1120: 2 нерешенных внешних

Смежные вопросы