Почему здесь важно использовать static_cast вместо reinterpret_cast? - PullRequest
9 голосов
/ 04 февраля 2012

В ответе на сообщение в блоге Рэймонда Чена ,

Спрашивающий указал

Рэймонд, я считаю, что пример C ++ неверен, посколькуположение подобъекта базового класса в производном классе не определено в соответствии со стандартом ISO C ++ 2003 (10-3, стр. 168), и вы предполагаете, что подобъект базового класса всегда находится в начале.Пример C тоже подойдет для C ++, поэтому я буду придерживаться его.

Раймонд ответил

[Код не делает этого предположения.Вот почему важно использовать static_cast вместо reinterpret_cast.Попробуйте: добавьте виртуальный метод в OVERLAPPED (чтобы vtable шел впереди) и посмотрите, что делает компилятор.-Рэймонд]

Я мог догадаться после прочтения его комментариев.Использование static_cast хорошо в этом примере, но reinterpret_cast нет.Потому что reinterpret_cast не конвертирует vtable.Правильно ли я понимаю?
Хотя, если я использую приведение C-Style там (не reinterpret_cast), оно также может пойти не так?

Я перечитал объяснение броска Более эффективного в C ++, чтобы понять это.Но ответа об этом не было.

1 Ответ

17 голосов
/ 04 февраля 2012

Использование static_cast нормально в этом примере, но reinterpret_cast - нет.Потому что reinterpret_cast не конвертирует vtable.

Нет, проблема в том, что reinterpret_cast полностью забывает о наследовании.Он просто вернет тот же адрес без изменений 1 .Но static_cast знает , что вы выполняете понижение: то есть приведение из базового класса к производному классу.Поскольку он знает оба задействованных типа, он соответствующим образом корректирует адрес, т. Е. Делает правильные вещи.

Давайте притворимся наша реализация выкладывает гипотетический класс OVERLAPPEDEX, который имеет виртуальную функцию, подобную этой:

+------+------------+------------------+-------------+
| vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------------+------------------+-------------+
       ^
       |
      ptr

Указатель, который нам дан, указывает на подобъект OVERLAPPED.reinterpret_cast не изменит это.Это только изменило бы тип.Очевидно, что доступ к классу OVERLAPPEDEX через этот адрес может привести к хаосу, потому что расположение его подобъектов теперь совершенно неверно!

       what we believe we have when we access OVERLAPPEDEX through the pointer
       +------+------------+------------------+-------------+
       | vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------+-----+------+-----------+------+------+------+
| vptr | OVERLAPPED | AssociatedClient | ClientState | <- what we actually have
+------+------------+------------------+-------------+
       ^
       |
      ptr

static_cast знает , что для преобразованияOVERLAPPED* до OVERLAPPEDEX* он должен корректировать адрес и делает правильные вещи:

 +------+------------+------------------+-------------+
 | vptr | OVERLAPPED | AssociatedClient | ClientState |
 +------+------------+------------------+-------------+
 ^
 |
ptr after static_cast

Хотя, если я использую там C-Style приведение (не reinterpret_cast),может ли это также пойти не так?

Приведение в стиле C определяется как первое из следующих успешных:

  1. const_cast
  2. static_cast
  3. static_cast, затем const_cast
  4. reinterpret_cast
  5. reinterpret_cast, затем const_cast

Asвы можете видеть, что static_cast пробуется раньше reinterpret_cast, поэтому в этом случае приведение в стиле C также будет делать правильные вещи.


Подробнее


1 Не гарантируется.Существует очень мало гарантий относительно того, что происходит на reinterpret_cast.Все известные мне реализации просто выдадут один и тот же адрес без изменений.

...