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


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

7 ответов:

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

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

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

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

class Scheduler
{
    ...

    void Scheduler::DetachEntityManager();

    ...
};


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

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

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

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

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

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

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

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

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

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

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

Инлайнинг имеет как минимум три "недостатка":

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

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

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

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

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

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

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