C ++ dynamic_cast - полиморфное требование и снижение - PullRequest
10 голосов
/ 10 января 2011

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

Если я перепутал вас с моим приведенным выше описанием, почему obj не должен быть полиморфным при обновлении, но при понижении должен быть полиморфным при использовании dynamic_cast?

class base
{
public:
    base()
    {
        cout<< " \n base constructor \n";
    }
};

class derived : public base
{
public:
    derived()
    {
        cout << " \n derived constructor \n";
    }
};

base *obj = dynamic_cast<base*> (new derived); // case 1: explicitly upcasting
derived *OBJ = dynamic_cast<derived*> (obj);   // case 2: error

Ответы [ 4 ]

18 голосов
/ 10 января 2011

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

Самый простой способ обойти это - пометить деструктор базового класса как виртуальный.

Upcasting (т.е.производная от базы) не нуждается в приведении, так как компилятор может проверить, будет ли приведение работать во время компиляции.Тем не менее, это не так при унынии.

6 голосов
/ 10 января 2011

Из 5.2.7 / 1 [expr.dynamic.cast]:

Результат выражения dynamic_cast<T>(v) является результатом преобразования выражения v в тип Т.

[...]

Если T "указатель на cv1 B" и v имеет тип "указатель на cv2 D", такой что B является базовым классом D, результатом является указатель на уникальный подобъект B объекта D, на который указывает v.

[...]

В противном случае , v должно быть указателем или значением l полиморфного типа.

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

struct B {};
struct D : B {};
void foo(D* dp)
{
    B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}
0 голосов
/ 04 марта 2016
B* b = new D();
D* d = dynamic_cast<D*>(b);

В приведенном выше примере большинство компиляторов реализуют динамическое приведение, проверяя, указывает ли указатель vtable на b на vtable производного класса D или нет.Если да, он просто возвращает адрес b в качестве возвращаемого значения, в противном случае он возвращает nullptr.Это то, что возможно происходит за кулисами, когда выполняется динамическое приведение: -

class car
{
    public:
    virtual void drive()
    {
         std::cout <<"car"<<std::endl;
    }
};
class toyota: public car
{
    public:
    virtual void drive()
    {
        std::cout <<"toyota"<<std::endl;
    }
};

class honda: public car
{
    public:
        virtual void drive()
    {
        std::cout <<"honda"<<std::endl;
    }
};

template <typename Tderived>
Tderived* dynamicCast(void* pBase)
{
    //compare the vptr of the class pointed by pBase with a temporary Tderived class. 
    //If vptr of pBase and vptr of Tderived() are pointing to the same vtable 
    //then it can be safely deduced that pBase is indeed pointing to an instance of Tderived
    if (*(int**)pBase == *(int**)&Tderived())
    {
        return (Tderived*)pBase;
    }
    else
    {
        return nullptr;
    }
}


int main()
{
    car* pCar;
    honda hondaCar;
    toyota toyotaCar;

    pCar = &toyotaCar;

    honda* pHonda = dynamicCast<honda>(pCar);
    if (nullptr != pHonda)
    {
        pHonda->drive();
    }
    else
    {
        toyota* pToyota = dynamicCast<toyota>(pCar);
        if (nullptr != pToyota)
        {
            pToyota->drive();
        }
    }
}

Теперь, если класс не полиморфен, компилятор не сможет определить, указывает ли pCar на honda илиавтомобиль тойота.Обратите внимание, что это только один из способов реализации dynamic_cast, поскольку стандарт C ++ ничего не говорит о vtables.

0 голосов
/ 10 января 2011

dynamic_cast

  • Используется для приведения базового указателя. в производный указатель. Если база указатель не указывает на объект тип производного, возвращает
  • Используется для приведения базовой ссылки. в производную ссылку. Если ссылка не указывает на объект из производного, он бросает станд :: bad_cast.
  • Можно считать проверенным составом эквивалентно static_cast, в том, что это проверяет, указал ли объект действительно имеет производный тип.

Вы должны прочитать больше о Dynamic_cast (с примером) там .

...