Влияет ли дополнительное наследование на структуру объекта или его создание? - PullRequest
3 голосов
/ 09 октября 2011

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

Чтобы добиться этого, я токенизирую специальные 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 ]

3 голосов
/ 09 октября 2011

Большинство, если не все приличные компиляторы реализуют Оптимизацию пустой базы (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 ). Вы можете обернуть эту проверку классификации в шаблоне вспомогательного класса.

3 голосов
/ 09 октября 2011

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

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

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

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

0 голосов
/ 09 октября 2011

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

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

...