Подтвердить что ты не робот

Определение конструктора в файле VS файла с расширением VS (.cpp)

Привет, спасибо за чтение. Это может быть просто подпадает под категорию "личные предпочтения", но в любом случае мы идем...

Я могу определить тело конструктора класса в файле .h или в файле реализации .cpp. Эти два стиля, вероятно, идентичны, поскольку компилятор связан с конкретным проектом (проект для меня означает dll). То же самое относится к любым функциям-членам: они могут быть определены в файле заголовка или просто объявлены там, а затем определены в файле cpp.

ОДНАКО...

Я обнаружил, что если мне нужно включить такие заголовочные файлы класса в разные проекты (это означает, что в конечном итоге код, который использует заголовочный файл, попадает в другую dll), то фактическая реализация в файле заголовка вызывает некоторые головные боли при компиляции (не со связыванием... я даже не добираюсь до этого). Зачем? Ну, я не стану слишком подробно, но компилятор, очевидно, пытается разрешить все функции, которые могут быть определены в других файлах заголовков и т.д.... заставляя бедного разработчика начать вытягивать различные файлы заголовков eetc...

L O N G S T O R Y короткий:

Разве не всегда лучше хранить файлы заголовков без какой-либо реализации и просто использовать их для "деклараций"? Это упростит включение их в более чем один проект без необходимости носить с собой много лишнего мусора.

Каково ваше мнение об этом? Спасибо!

4b9b3361

Ответ 1

Держите свои заголовки свободными от реализаций, если вы не хотите, чтобы реализации были встроены (например, тривиальные геттеры/сеттеры). И если они не являются шаблонами, конечно.

Я не вижу причины создавать исключение для конструкторов. Поместите их в файл .cpp.

Ответ 2

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

class A {
  public:
    A();
};

A::A() {
  // constructor body
}

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

class A {
  public:
    inline A();
};

inline A::A() {
  // constructor body
}

Или:

class A {
  public:
    inline A() { // inline isn't required here, but it a good style
     // constructor body
    }
};

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

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

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

Ответ 3

Еще одно замечание: любые изменения в заголовочном файле требуют пересортировки всех файлов, содержащих этот заголовочный файл. Большинство систем сборки будут восстанавливать исходные файлы (*.cpp/.cc), которые зависят от измененного файла заголовка.

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

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