Реальный пример динамической трансляции в C ++ - PullRequest
12 голосов
/ 02 июля 2011

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

Если ограничение слишком сильное, неплохо было бы также использовать пример, где dynamic_cast обычно является способом.

Я бы хотел увидеть реальные примеры вместо того, чтобы "он обычно используется для приведения типов между деревьями типов вверх и вниз".

Ответы [ 5 ]

6 голосов
/ 02 июля 2011

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

То есть двойная диспетчеризация является инвазивной для участвующих классов, тогда как dynamic_cast работает без знания состава в классах.

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

Наконец, двойная отправка не происходит без собственных головных болей

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

1 голос
/ 02 июля 2011

Слишком сильное ограничение «вообще нельзя обойти». Любая функция C ++ может быть эмулирована в C. Все, что вам нужно сделать, чтобы обойти эту функцию, так сказать, использовать этот код C в C ++. Например, MFC, библиотека, созданная в глубине времен до стандартизации языка 1998 года, предлагала и по-прежнему предлагает свой тип динамического приведения.

Одним из примеров, когда вам обычно требуется динамическое приведение, является шаблон посетителя, например, как используется для обработки событий. Идея посещения состоит в том, чтобы централизовать динамическое приведение, чтобы вместо одного миллиарда динамических приведений во всем коде был один:

#include <stdio.h>

void say( char const s[] ) { printf( "%s\n", s ); }

struct Event
{
    struct Handler
    {
        virtual void onEvent( Event& ) = 0;
    };

    virtual void dispatchTo( Handler& aHandler )
    {
        aHandler.onEvent( *this );
    }

    template< class SpecificEvent >
    static void dispatch( SpecificEvent& e, Handler& aHandler )
    {
        typedef typename SpecificEvent::Handler SpecificHandler;

        // The single dynamic cast:
        if( SpecificHandler* p = dynamic_cast<SpecificHandler*>( &aHandler ) )
        {
            p->onEvent( e );
        }
        else
        {
            e.Event::dispatchTo( aHandler );
        }
    }
};

struct FooEvent
    : Event
{
    struct Handler
    {
        virtual void onEvent( FooEvent& ) = 0;
    };

    virtual void dispatchTo( Event::Handler& aHandler )
    {
        dispatch( *this, aHandler );
    }
};

struct Plane
    : Event::Handler
{
    virtual void onEvent( Event& ) { say( "An event!" ); }
};

struct Fighter
    : Plane
    , FooEvent::Handler // Comment out this line to get "An event!".
{
    virtual void onEvent( FooEvent& ) { say( "Foo Fighter!" ); }
};

void doThingsTo( Plane& aPlane )
{
    FooEvent().dispatchTo( aPlane );
}

int main()
{
    Fighter plane;

    doThingsTo( plane );
}

Вывод этой программы Foo Fighter!.

Как уже упоминалось, это упрощено. Реальность имеет тенденцию быть немного более грязной. И с гораздо большим количеством кода.

Приветствия и hth.

0 голосов
/ 02 июля 2011

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

0 голосов
/ 02 июля 2011

Допустим, у нас есть библиотека, которую мы используем, и она предназначена для получения некоторых типов из:

class A {};
class B : public A {};
class C : public B {};

И когда мы выводим наши типы, у нас есть некоторые вещи, которые являются общими для всех наших случаев:

class CommonStuff {};
class D : public CommonStuff, public C {};

Теперь, когда мы работаем с нашей библиотекой, и существует обратный вызов, который принимает тип A & (или B & или C &)

void some_func(A& obj);

И предположим, что в этой функции она ожидает полиморфное поведение, но нам нужно получить доступ к некоторым из наших CommonStuff:

void some_func(A& obj)
{
    CommonStuff& comn = dynamic_cast<CommonStuff&>(obj);
}

Поскольку прямой корреляции между A и CommonStuff нет, мы не можем использовать static_cast, reinterpret_cast, очевидно, не является правильным выбором, так как это приведет к нарезке. Единственный вариант здесь dyanmic_cast.

Теперь возьмите это с крошкой соли, потому что это можно обойти.

0 голосов
/ 02 июля 2011

Я лично использую его для проработки определенных частей моего игрового движка. У меня есть базовый класс сущностей, из которого я получаю различные другие сущности. Я приведу их к типу базового класса, чтобы легко хранить их в связанном списке. Когда я хочу проверить, является ли конкретная запись в моем списке определенной сущностью, я динамически передаю ее этому типу. Если он возвращает ноль, то я знаю, что это не так.

...