Каков оптимальный порядок членов в классе?

Если у меня есть следующий класс:

class Example
{
  bool m_b1;
  SomeClass m_sc;  
  bool m_b2;
  char * m_name;
  bool m_b3;
  int m_i1;
  SomeClass * m_sc;
  int m_i2;
};

каков оптимальный порядок членов? Насколько мне известно, порядок объявления членов равен порядку членов в памяти при создании экземпляра этого класса.

  • должен ли я сортировать члены так, чтобы равные типы были вместе?
  • все указатели считаются одним и тем же типом?
  • влияет ли перестановка на размер объекта? Применяется 4 или 8 байт выравнивания здесь?
  • как я могу видеть последствия вышесказанного? Показывает ли sizeof память, используемую объектом, включая пустое пространство для выравнивания?
  • лучше ли в конце дня сортировать членов так, чтобы их смысл и контекст было легче понять?

5 ответов


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

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


порядок важен, когда переменная-член зависит от другой переменной-члена при использовании списков инициализаторов:

представьте себе класс с двумя переменными, где конструктору второй переменной (varC) нужен первый (varB)

class A
{
    B varB;
    C varC;

    A()
    : varC(varB)
    {
    }
};

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

конечно, стиль и читаемость также важный.


должен ли я сортировать члены так, чтобы равные типы были вместе?

Если это увеличивает читаемость, да!

все ли указатели считаются одинаковыми?

Да, все указатели должны принимать 32bit или 64bit или все, что определяет система.

влияет ли перестановка на размер объекта? Делает 4 или Выравнивание 8 байтов применяется здесь?

это возможно, из-за выравнивания. видео.

лучше ли в конце дня сортировать членов так, чтобы их смысл и контекст легче понять?

если это увеличивает значение, читаемость и ремонтопригодность, да.


хорошее эмпирическое правило, которое я узнал, - "сначала поместите самые большие переменные".

в вашем примере это будет

   class Example
{

  SomeClass m_sc; 
  SomeClass * m_sc;
  char * m_name;
  int m_i1;
  int m_i2;
  bool m_b1;
  bool m_b2;
  bool m_b3;
};

Я не лучший в формулировании моих мыслей, так что потерпите меня.

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


В дополнение к ответам упоминалось ранее:

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

очень хорошая статья, касающаяся ориентированного на данные дизайна, можно найти здесь:ориентированный на данные дизайн (или Почему вы можете стрелять себе в ногу с ООП)