reinterpret_cast и виртуальный между несвязанными типами - PullRequest
1 голос
/ 27 марта 2012

Если бы кто-нибудь любезно объяснил, почему работает следующий фрагмент кода, я протестировал его на Visual Studio .NET 2008, g ++ на Cygwin и ideone.com . Более важно, я хотел бы знать, если это действительно. Обратите внимание, что A и B не связаны между собой.

Редактировать: после комментария @ leftaroundabout я внес следующие изменения в свой код

#include <iostream>
#include <cstdlib>

class A
{
public:
    virtual void Bar()
    {
        std::cout << "A::Bar() -> " << this << std::endl;
    }

    virtual void Foo()
    {
        std::cout << "A::Foo() -> " << this << std::endl;
    }   
};

class B
{
public:
    virtual void Foo()
    {
        std::cout << "B::Foo() -> " << this << std::endl;
    }
};

int main()
{
    B* b = reinterpret_cast<B*>( new A );
    b->Foo();   
    return EXIT_SUCCESS;
}

Программа выводит сообщение:

A::Bar() -> 0x9806008

По сути, первый виртуальный метод вызывается независимо от того, как он называется.

Ответы [ 4 ]

3 голосов
/ 27 марта 2012

Он просыпается только по счастливой случайности, в стандарте ничего не говорится о том, что он должен работать - актерский состав недействителен.Компилятор, скорее всего, разместит оба класса одинаково в памяти, но AFAIK такого обязательства не существует.

Попробуйте добавить:

virtual void Bar()
{
    std::cout << "A::Bar() -> " << this << std::endl;
}

перед Foo в Aи посмотрим, что произойдет - шансы на Bar будут вызваны при запуске b->Foo().

1 голос
/ 27 марта 2012

reinterpret_cast<> в основном отключает любые проверки безопасности типов и говорит компилятору «не проверяйте это, я знаю, что я делаю».

Страница Microsoft на reinterpret_cast рассказывает так же, как и все остальные;

Результат reinterpret_cast нельзя безопасно использовать ни для чего кроме того, чтобы быть возвращенным к его первоначальному типу. Другое использование, в лучший, непортативный.

0 голосов
/ 27 марта 2012

Из стандарта 5.2.10 / 7

Указатель на объект может быть явно преобразован в указатель на объект другого типа. 65) За исключением преобразования значения типа указательк T1 »к типу« указатель на T2 »(где T1 и T2 являются типами объектов, а требования к выравниванию T2 не более строгие, чем требования к T1) и обратно к своему первоначальному типу дает исходное значение указателя, результат такогопреобразование указателя не определено.

Это означает, что единственное, что гарантировано, это то, что если вы приведете A к B и затем вернетесь к A, вы получите исходный указатель обратно:

  A* a = reinterpret_cast<A*>(reinterpret_cast<B*>( new A ));
  a->Foo();  //Ok

Все остальные виды использования не определены.

0 голосов
/ 27 марта 2012

Недействительно;разыменование указателя, который был приведен к неправильному типу, приводит к неопределенному поведению.

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

...