С-стиль upcast и downcast с участием частного наследования - PullRequest
6 голосов
/ 10 мая 2009

Рассмотрим следующий фрагмент кода: -

class A {};

class B : private A {};

B* bPtr1 = new B;
// A* aPtr1 = bPtr1; // error
// A* aPtr2 = static_cast<A*>(bPtr1); // error
A* aPtr3 = (A*)bPtr1;
B* bPtr2 = (B*)aPtr3;

Приведение в стиле C отбрасывает личное наследование, в то время как неявное и static_cast терпит неудачу (также dynamic_cast). Зачем ? Если приведение в стиле C - это просто бит, как реализовано приведение в C ++, т. Е. Как они узнают тип наследования из объема памяти?

После того, как bPtr1 приведен к aPtr3, мне придется использовать другое приведение в стиле C, чтобы понизить до B, так как снова и static_cast, и dynamic_cast не пройдены. Итак, bPtr2 гарантированно будет хорошим?

Заранее спасибо

Ответы [ 4 ]

7 голосов
/ 10 мая 2009

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

Таким образом, dribeas прав, компиляторы обязаны правильно обрабатывать преобразование указателя в стиле C в OP, даже когда B наследуется от нескольких базовых классов. Мое собственное тестирование с MSVC ++ 8 и MinGW подтверждает его результаты на практике - когда B наследует от нескольких базовых классов, компилятор будет корректировать указатели при преобразовании B* в A* или наоборот, так что правильный объект или подобъект идентифицирован.

Я настаиваю на своем утверждении, что вы должны получить B публично из A, если вы когда-нибудь намереваетесь трактовать B как A, поскольку вместо этого используется личное наследование требует с использованием бросков в стиле C.

0 голосов
/ 10 мая 2009

Если приведения в стиле C просто бесполезны, как реализованы приведения в C ++, т. Е. Как они узнают тип наследования из памяти?

Приведения в C-стиле в C ++ означают больше, чем кажется на первый взгляд. Технически это единственный способ попросить компилятор выбрать правильный оператор приведения, необходимый для преобразования. По той же причине это лучше, чем использовать reinterpret_cast. Последнее определяется реализацией.

Кажется, что в забросках в стиле C много путаницы. Помните, что ни один инструмент не является безопасным, пока вы не знаете, как его использовать. То же самое относится к броску в стиле C. Смысл приведения в стиле C состоит в том, чтобы попросить компилятор выбрать наиболее безопасное и наиболее переносимое преобразование для данной пары типов. Это может молча вызвать изменение с static_cast на reinterpret_cast и привести к ошибкам. Дело в том, что вам нужно знать, что вы делаете.

0 голосов
/ 10 мая 2009

Существующие ответы великолепны, но одна часть информации, которую вы можете найти полезной, относится к этому:

Брошюры в стиле C просто заигрывают

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

0 голосов
/ 10 мая 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...