Встраивать или не встраивать - PullRequest
10 голосов
/ 01 августа 2011

В последнее время я пишу несколько классов; и мне было интересно, является ли это плохой практикой, плохой для производительности, нарушает ли инкапсуляцию или есть что-то еще изначально плохое с фактически определением некоторых меньших функций-членов внутри заголовка (я попробовал 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;
};

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

(Кроме того, я не уверен, будет ли это более подходящим для сайта «обзора кода»)

Ответы [ 7 ]

11 голосов
/ 01 августа 2011

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

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

6 голосов
/ 01 августа 2011

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

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

class Scheduler
{
    ...

    void Scheduler::DetachEntityManager();

    ...
};


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

На мой взгляд, это более читабельно.

2 голосов
/ 01 августа 2011

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

  1. Отличает тривиальные методы от нетривиальных.
  2. Он позволяет сразу увидеть эффект тривиальных методов, являющихся самодокументированным кодом.

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

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

1 голос
/ 01 августа 2011

Встраивание имеет как минимум три «недостатка»:

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

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

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

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

1 голос
/ 01 августа 2011

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

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

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

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

Edit: Что касается стиля и инкапсуляции, использование встроенных методов не позволяет использовать такие вещи, как указатель для реализации, пересылку объявлений и т. Д., Поскольку ваш код находится в заголовке.

0 голосов
/ 01 августа 2011

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

0 голосов
/ 01 августа 2011

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

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

...