2012-02-04 4 views
9

At a reply of a blog post of Raymond Chen,Почему здесь важно использовать static_cast вместо reinterpret_cast?

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

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

Raymond ответил

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

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

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

+2

Я вообще не разбираюсь в C++, но я понимаю, что «реинтерпрет-литье» означает именно то, что '* (destination_type *) &' будет означать в C. Предположительно «статический бросок» фактически принимает классовые отношения в и позволяет компилятору выполнять нетривиальное преобразование. –

ответ

17

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

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

Давайте притвориться наша реализация выкладывает гипотетический 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

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


More info


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

+0

Да, мой 'convert vtable' был очень странным предложением. Кстати, ли заклинатель C-стиля также знает отношения наследования? Возможно, это не так. Но я всегда использовал C-стиль, даже если я использую C++. И я думаю, что на этом примере он хорошо работал. Это странно. – Benjamin

+1

@Benjamin Я добавил объяснение о приведениях в стиле C и почему он работает в этом случае. Я бы рекомендовал не использовать их, потому что не всегда ясно, какая из альтернатив выбрана. Случай в стиле C - очень тупой молот. –

+0

Конечно, я буду следовать вашим рекомендациям, потому что я это понимаю сейчас. Спасибо. Тем не менее, стандартизирован ли ваш заказ заклинателя C-Style? – Benjamin