Подтвердить что ты не робот

Отражает ли публичное и частное влияние на макет памяти объекта?

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

Изменяет ли что-либо (кроме видимости), если я организовываю членов таким образом, чтобы общественность, защищенная и приватная менялись?

class Example
{
public:
  SomeClass m_sc; 
protected:
  char m_ac[32];      
  SomeClass * m_scp;
private:
  char * m_name;
public:
  int m_i1;
  int m_i2;
  bool m_b1;
  bool m_b2;
private:
  bool m_b3;
};

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

Я предполагаю, что он вообще не влияет на скомпилированную программу, так же как const проверяется только во время компиляции. Правильно ли это?

4b9b3361

Ответ 1

Ответ зависит от языковой версии, потому что это изменилось с С++ 03 на С++ 11.

В С++ 03 это правило было:

Члены одного и того же блока управления доступом (то есть от одного из ключевых слов public, protected, private к следующему из этого набора) должны выделяться в порядке объявления внутри класса, не обязательно смежно.

В С++ 11 правило было изменено на следующее:

Члены с одним и тем же уровнем контроля доступа (public, protected, private) должны быть выделены в порядке декларации внутри класса, не обязательно смежно.

Итак, в С++ 03 вы можете гарантировать это (я использую @ для обозначения смещения члена внутри класса):

  • @m_ac < @m_scp
  • @m_i1 < @m_i2 < @m_b1 < @m_b2

В С++ 11 у вас есть еще несколько гарантий:

  • @m_ac < @m_scp
  • @m_sc < @m_i1 < @m_i2 < @m_b1 < @m_b2
  • @m_name < @m_b3

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


Обратите внимание, что есть еще один механизм, который может войти в изображение: классы стандартного макета.

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

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

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

Ответ 2

Я бы сказал, что правило порядка не всегда лучшее. Единственное, что вы делаете, это избегать "заполнения".

Однако другое "правило", которое следует придерживаться, содержит ваши самые "горячие" члены сверху, чтобы они могли вписываться в строку кэша, которая обычно составляет 64 байта.

Представьте, что у вас есть цикл, который проверяет флаг вашего класса, и его смещение равно 1, а ваш другой член - в смещении 65, а другой - в смещении 200. Вы получите пропуски кэша.

int count = 0;

for (int i = 0; i < 10; i++)
{
     if (class->flag/*offset:1*/ == true && class->flag2  == true/*offset:65*/)
             count += class->n; /*offset: 200*/            
}

Этот цикл будет намного медленнее, чем версия, похожая на кэш, например:

int count = 0;

for (int i = 0; i < 10; i++)
{
     if (class->flag/*offset:1*/ == true && class->flag2  == true/*offset:2*/)
             count += class->n; /*offset: 3*/

}

Последний цикл должен читать только одну строку кеша на итерацию. Он не может ускориться.