Влияет ли дополнительное наследование на структуру объекта или его создание?

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

Чтобы добиться этого, я делаю особый токенизатор class es в исходном коде, вставив в них наследование с пустым struct:

struct _special {};  // empty class
class A : public _special {  // A becomes special
...
};
class B {  // 'B' remains normal
...
};
class D : public A {  // 'D' becomes special due to 'A'
...
};

При необходимости я могу найти отдельные и обычные классы, используя is_base_of<Base,Derived>, Альтернативным способом было бы использовать typedef внутри специальных классов:

class A {
  public: typedef something _special;
};

Проблема в том, что если A дитя наследуют от нескольких классов тогда будет неоднозначно typedef s.

Вопрос: с добавлением такого интерфейса как наследование с пустым class _special Повредит ли это текущему коду каким-либо образом (например, структурирование объекта, ошибка компиляции и т. д.)?

3 ответа

Большинство, если не все достойные компиляторы реализуют Оптимизацию пустой базы (EBO) для простых случаев, что означает, что размеры вашего объекта не будут расти за счет наследования от пустой базы. Однако, когда класс наследует от пустой базы более чем одним способом, оптимизация может быть невозможна из-за необходимости иметь разные адреса для разных пустых баз одного и того же типа. Чтобы защититься от этого, обычно делают пустую базу шаблоном, берущим производный класс в качестве аргумента, но это сделает is_base_of непригодным для использования.

Лично я бы внедрил эту классификацию внешне. Специализация шаблона не приведет к желаемому результату для классов, производных от специальных, косвенно считающихся также специальными. Похоже, вы используете C++11, поэтому я бы сделал:

std::false_type is_special( ... );
std::true_type is_special( A const* );

И заменить is_base_of<T, _special> с decltype( is_special( static_cast<T*>(0) ) ), В C++03 то же самое может быть достигнуто с помощью sizeof Хитрость в том, что функция классификации возвращает типы разных размеров:

typedef char no_type;
struct yes_type { no_type _[2]; };

no_type is_special( ... );
yes_type is_special( A const* );

И заменить is_base_of<T, _special> с sizeof( is_special( static_cast<T*>(0) ) ) == sizeof( yes_type ), Вы можете обернуть эту проверку классификации в шаблоне вспомогательного класса.

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

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

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

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

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

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

Другие вопросы по тегам