Где я должен реализовать метод класса?

Я знаю о трех разных вариантах реализации "местоположения" для моих методов класса:

1) Определите метод внутри моего класса (файл .h) и реализуйте его в моем файле .cpp

//.h
class Foo
{
  int getVal() const;
};


//.cpp
int Foo::getVal() const
{ return 0; }

2) Определите и реализуйте метод внутри моего класса (файл .h).

//.h
class Foo
{
  int getVal() const
  { return 0; }
};

3) Определите метод внутри моего класса и реализуйте его вне класса, но внутри моего файла заголовка.

//.h
class Foo
{
  int getVal() const;
};

int Foo::getVal() const
{ return 0; }

Каковы основные различия между этими тремя подходами?

Ответ 1

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

Метод one предоставляет только интерфейс для функции в файле заголовка. Это означает, что вы показываете хороший чистый интерфейс, и ваша реализация не отображается в открытом тексте. Тем не менее, код не может быть встроен в единицы компиляции, поэтому он может быть немного медленнее во время выполнения (на практике это имеет значение только для очень-очень маленького процента кода). ЭТО ДОЛЖНО БЫТЬ ВАШИМ ПУТЕМ ПО УМОЛЧАНИЮ.

Метод 2 подразумевает вложение. Длинные функции будут загромождать ваш класс, который (imho) плох. Также раскрывает вашу реализацию миру. Тем не менее, функция может быть встроена и менее сложна, чем определение ее в другом месте. Я резервирую это для очень маленьких функций.

Метод 3 фактически является незаконным, так как вы нарушаете правило с одним определением, но все в порядке:

//Foo.h
class Foo {
  int getVal() const;
};

inline int Foo::getVal() const { 
  return 0; 
}

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

Ответ 2

(1) будет быстрее компилироваться для большого проекта (нужно только компилировать определение getVal один раз в Foo.cpp и только перекомпилировать одну вещь, если определение изменится), и вы получите очень понятный интерфейс для класс для людей, которые хотят его найти. С другой стороны, вы не можете встроить getVal().

(2) и (3) будут скомпилировать медленнее и добавить дополнительные зависимости к изменениям ваших определений. Но вы можете inline getVal(). Также это необходимо, если getVal является функцией шаблона. ПРИМЕЧАНИЕ (3) вызовет ошибки компоновщика, если ваш заголовок включен несколько раз - вам нужно отметить, чтобы не помечать вашу функцию inline. Это хорошая причина предпочесть (1) и (2) - (3).

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

Ответ 3

Небольшое примечание перед тем, как я начну, есть много слов, которые очень похожи для описания этих вещей, я буду использовать объявление для части в файле заголовка (int getVal() const) и реализацию для части в cpp файл (int Foo::getVal() const). Извините, если они не совсем точны.

Также обратите внимание, что преимущества - это штрафы для других, если это не ясно.

1) Определите метод внутри моего класса (файл .h) и реализуйте его в моем файле .cpp

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

Отделяет декларацию от реализации. Это дает несколько преимуществ:

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

2) Определите и реализуйте метод внутри моего класса (файл .h).

Это называется встроенной реализацией, она должна использоваться только в простых реализациях. Он также имеет несколько преимуществ:

  • Большинство компиляторов воспринимают это как огромный встроенный намек. Это не гарантирует inling, но это очень вероятно, что для простых методов может быть победой. Способы стиля собственности - общий пример.
  • Ваша реализация тривиально найти, поскольку она прямо в декларации.

3) Определите метод внутри моего класса и реализуйте его вне класса, но внутри моего файла заголовка.

Я раньше этого не видел, но предполагал, что он используется, когда определение нетривиально, но вы хотите использовать встроенные преимущества 2. IdeaHat упоминает, что в этом случае требуется ключевое слово inline.