Встраивать или не встраивать

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

class Scheduler {
public:
    typedef std::list<BSubsystem*> SubsystemList;

    // Make sure the pointer to entityManager is zero on init
    // so that we can check if one has been attached in Tick()
    Scheduler() : entityManager(0) { }

    // Attaches a manager to the scheduler - used by Tick()
    void AttachEntityManager( EntityManager &em )
        { entityManager = &em; }

    // Detaches the entityManager from a scheduler.
    void DetachEntityManager()
        { entityManager = 0; }

    // Adds a subsystem to the scheduler; executed on Tick()
    void AddSubsystem( BSubsystem* s )
        { subsystemList.push_back(s); }

    // Removes the subsystem of a type given
    void RemoveSubsystem( const SubsystemTypeID& );

    // Executes all subsystems
    void Tick();

    // Destroys subsystems that are in subsystemList
    virtual ~Scheduler();
private:
    // Holds a list of all subsystems
    SubsystemList subsystemList;

    // Holds the entity manager (if attached)
    EntityManager *entityManager;
};

Итак, есть ли что-то действительно неправильное с такими функциями вложения, или это приемлемо?

(Кроме того, я не уверен, что это больше подходит для сайта "обзор кода" )

Ответ 1

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

Есть несколько исключений: я всегда буду устанавливать виртуальный деструктор абстрактный базовый класс, если все остальные функции являются чисто виртуальными; кажется глупым иметь отдельный исходный файл только для пустого деструктор, и если все остальные функции являются чисто виртуальными и нет данных, деструктор не изменится без что-то еще меняется. И я иногда предоставляю встроенные конструкторы для "структур" классов, в которых все члены данных являются публичными, а других функций нет. Я также менее строг об избежании inline в классах, которые определены в исходном файле, а не заголовок, проблемы связи, очевидно, не применяются в в этом случае.

Ответ 2

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

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

class Scheduler
{
    ...

    void Scheduler::DetachEntityManager();

    ...
};


inline void Scheduler::DetachEntityManager()
{
    entityManager = 0;
}

По-моему, это более читаемо.

Ответ 3

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

  • Он отличает тривиальные методы от нетривиальных.
  • Это позволяет получить тривиальные методы с первого взгляда, будучи самодокументируемым кодом.

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

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

Ответ 4

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

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

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

Встраиваемые методы являются прекрасными, если вы знаете, где их использовать и не спам.

Изменить: Что касается стиля и инкапсуляции, использование встроенных методов не позволяет вам использовать такие вещи, как указатель на реализацию, передовые декларации и т.д., Так как ваш код находится в заголовке.

Ответ 5

Вложение имеет три "недостатка", по крайней мере:

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

  • ваш двоичный код будет больше;

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

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

Ответ 6

Методы внутри тела class обычно inline автоматически. Кроме того, inline - это предложение, а не команда. Компиляторы обычно достаточно умны, чтобы судить о том, есть ли функция inline или нет.

Вы можете обратиться к этому аналогичному вопросу.

Ответ 7

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