С++ приведение базового класса к беспорядку производного класса

Если бы мне пришлось создать базовый класс с именем base и производные классы с именами derived_1, derived_2 и т. д. Я использую коллекцию экземпляров базового класса, то, когда я извлекаю элемент и пытаюсь его использовать, я бы обнаружил, что С++ считает, что это type относится к базовому классу, вероятно, потому, что я получил его из std::vector base. Это проблема, когда я хочу использовать функции, которые существуют только для конкретного производного класса, тип которого, как я знал, был этим объектом, когда я помещал его в вектор.

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

(derived_3)obj_to_be_fixed;

И вспомнил, что это указатель. После некоторой настройки это теперь сработало.

*((derived_3*)&obj_to_be_fixed);

Это правильно или есть, например, функция abc_cast(), которая делает это с меньшим беспорядком?

редактировать:

Мне пришлось расширить это до другого вопроса, там показаны полные решения. stackoverflow.com ... почему-the-polymorphic-types-error- и-уборка-вопрос


person alan2here    schedule 08.01.2011    source источник
comment
Подождите, это std::vector< base > или std::vector< base * >? Потому что в первом случае, если вы храните объекты производного класса, вероятно, также происходит некоторая нарезка объектов...   -  person Matteo Italia    schedule 08.01.2011
comment
Ой. Как правило, вам не следует этого делать, потому что, как уже было сказано, у вас будет нарезка объектов (en.wikipedia. org/wiki/Object_slicing), который фактически сводит все ваши объекты к экземплярам базового класса; если вы хотите хранить объекты различных производных классов одной и той же иерархии классов, вы должны использовать std::vector< base *>; это сохранит объекты нетронутыми, хотя вы все равно получите их как указатели на базовый класс (см. Ответ @Peon the Great, чтобы узнать, как с ними бороться).   -  person Matteo Italia    schedule 08.01.2011
comment
Спасибо за информацию о нарезке объектов, такие вещи не понятны в C++. Вместо этого я изменю свою карту, чтобы она содержала указатели.   -  person alan2here    schedule 08.01.2011
comment
(derived_3&)obj_to_be_fixed может показаться, что он работает, но это не так!   -  person Oliver Charlesworth    schedule 08.01.2011
comment
Понял, Оли. Спасибо. Теперь я собираюсь использовать решение std::vector‹base*›.   -  person alan2here    schedule 08.01.2011


Ответы (4)


Если вы храните свои объекты в std::vector<base>, просто нет возможности вернуться к производному классу. Это связано с тем, что производная часть была нарезана при ее сохранении в экземпляре базового класса (в конце концов, ваш вектор содержит копии ваших данных, поэтому он успешно копирует только базовую часть ваших объектов), что делает сохраненный объект истинным экземпляром базовый класс вместо производного класса, используемого в качестве базового класса.

Если вы хотите хранить полиморфные объекты в векторе, сделайте его std::vector<base*> (или какой-нибудь умный указатель на базу, но не саму базу) и используйте dynamic_cast<derived_3*>, чтобы привести его к правильному типу (или static_cast, если он чувствителен к производительности и вы уверены достаточно того, что вы пытаетесь привести к правильному типу (в этом случае, если вы ошибетесь, произойдут ужасные вещи, так что будьте осторожны)).

person Grizzly    schedule 08.01.2011
comment
Насколько я знаю, кастинг считается плохой практикой. Итак, как можно извлечь полиморфные объекты из контейнера без дурной практики? - person Fataho; 26.10.2019

Если вы используете vector из base, то все ваши экземпляры являются base экземплярами, а не производными экземплярами.

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

person CB Bailey    schedule 08.01.2011
comment
По-видимому, согласно другим комментариям, я должен использовать std::vector‹base*›, чтобы предотвратить нарезку. Векторы могут содержать смесь типов, где, если вы знаете, какой тип элемента, вы можете получить этот элемент и делать с ним определенные действия. Это один из способов сделать это. - person alan2here; 08.01.2011
comment
@alan2here: векторы могут содержать смесь типов. Боюсь, это просто неправда. Если вы используете std::vector< base* >, у вас есть вектор указателей. Указатели могут указывать на объекты разных типов, но сами указатели относятся к одному и тому же типу. И теперь, поскольку ваш вектор представляет собой вектор указателей, вы знаете, что должны управлять тем, где хранятся объекты, на которые указывает этот указатель, потому что объекты больше не хранятся в векторе. - person CB Bailey; 08.01.2011
comment
Спасибо. Я неправильно выразился. Я внутренне не знал, что здесь происходит. Хороший момент о том, что теперь нужно управлять большим количеством вещей. - person alan2here; 08.01.2011
comment
@alan2here: Вот почему у нас есть boost::ptr_vector‹base›, который будет управлять объектами ptr вместо вас. - person Martin York; 08.01.2011

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

Если вам действительно нужно привести к производному типу, используйте оператор dynamic_cast.

person Peon the Great    schedule 08.01.2011
comment
То, что вы говорите, правильно, но оно не решает самую серьезную проблему, то есть происходящее нарезку объекта. - person Oliver Charlesworth; 08.01.2011
comment
Я предполагаю, что указатели используются в векторе, когда я вижу этот вопрос. ИМО, бросающее экземпляры объекта в вектор напрямую, является плохим дизайном. Распределители стандартных библиотек гарантируют только и тестировались только с примитивами и стандартными типами библиотек. Что-нибудь кроме них, указатели - лучший выбор. - person Peon the Great; 08.01.2011
comment
Ну, суть вопроса в том, что указатели не используются в векторе. ;) а также что? Контейнеры стандартных библиотек предназначены для хранения значений, а не указателей. Часто хранение указателей в контейнере является плохим решением. - person jalf; 09.01.2011

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

Ваше приведение по указателям — это не что иное, как просто хак, который переинтерпретирует память, занятую объектом base, как объект derived. Это совершенно бессмысленно и может «сработать» только случайно.

person AnT    schedule 08.01.2011