Проблема состава и наследования - PullRequest
0 голосов
/ 26 июля 2011

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

class A{
    public : 
    virtual void foo() = 0 ;
};


class B{
    public:
        void setA(A * a) { m_a = a ;}

        virtual void f() = 0;
        void g() {m_a->foo();}
    protected:
        A * m_a ;
 };

Чтобы использовать библиотеку, вам просто нужно извлечь классы и реализовать чисто виртуальные функции, такие как foo(), и предоставить другие методы, специфичные для реализации (bar()).

class dA : public A {
    public :
         void foo() {/* ... */}
         void bar() {/* ... */}
};

class dB : public B {
     public :
          void f() ;
};

Обычно я использую этот класс, вызывая

dB * mydB = new dB() ;
mydB->setA(new dA() );
mydB->f() ;
mydB->g() ;

Но у меня проблема с дизайном при реализации dB::f(), потому что мне нужно вызвать dA::bar(), что характерно для dB. Но в классе я держу ссылку только на дБ через B *. Тогда я подумал о двух вариантах:

  • используйте dynamic_cast каждый раз, когда f() вызывается для приведения B::m_a в dB*
  • добавить член m_dA в дБ, который хранит тот же указатель, что и m_a, но может использоваться для доступа к определенным функциям в дБ.

Конечно, я не могу изменить базовые классы.

Я хотел бы знать, есть ли более элегантное решение этой проблемы (например, шаблон проектирования, о котором я не думал). Если нет, какой мне выбрать?

Ответы [ 3 ]

2 голосов
/ 26 июля 2011

У вас есть третье решение. Добавьте функцию setA() в dB. Конечно, эта функция будет скрывать B::setA(), что хорошо для вас, если dB::setA() реализовано как:

class dB : public B 
{
     dA *m_dA ; //add this member also!
     public :
          void f()
          {
             m_dA->bar(); //fast forward : no cast here!
          }
          void setA(A *a) //this hides B::setA()
          {
             m_dA= dynamic_cast<dA*>(a); //just one time dynamic cast!
             if ( m_dA == 0 )
             {  
                 throw std::runtime_error("invalid argument");
             }
             B::setA(a); //now call the hidden function in the base!
          }
};

Таким образом, вам не нужно dynamic_cast каждый раз, когда вы звоните dB::f(), что делает вызов быстрым!

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

dB как конкретный класс не должен вызывать методы для dA несвязанного конкретного класса. Даже с reinterpret_cast это плохой дизайн, который без необходимости связывает несвязанные объекты. Общая функциональность должна быть помещена в общий базовый класс или интерфейс.

Например, класс A можно рассматривать как интерфейс, поскольку он не имеет ничего, кроме чисто виртуальных методов. Поэтому было бы безопасно использовать множественное наследование, если вы хотите, чтобы этот интерфейс также был на dB. В этом случае вам, конечно, придется реализовать свой собственный foo.

class dB : public B, public A {
     public :
          void f();
          void foo();
};

Если вам нужен bar(), и по какой-то причине вы можете изменить интерфейс A, тогда создайте свой собственный интерфейс, обеспечивающий чисто виртуальную функцию bar(), а затем наследуйте dA и dB от вашего нового интерфейса и соответственно реализуйте bar() и используйте указатели интерфейса для этого.

Если вы должны использовать один в терминах другого, то композиция - это путь, а не неоднозначный базовый указатель, который может дать сбой при динамическом приведении. Сделайте конкретный dA элемент в пределах dB возможно.

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

Ваш дБ может работать только в том случае, если его значение действительно равно дА.Вы должны убедиться, что это всегда так.

Следовательно, переопределите метод setA, чтобы использовать dynamic_cast, чтобы убедиться, что это действительно dA.Теперь неважно, сохраняете ли вы в этот момент результат в m_dA или динамическом приведении позже.Меньше вероятности наличия неправильно инициализированного дБ, динамическое приведение которого может закончиться позже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...