О множественном наследовании и определении виртуальной функции

#c #multiple-inheritance #virtual-functions

#c #множественное наследование #виртуальные функции

Вопрос:

У меня есть сценарий множественного наследования без виртуальных базовых классов, подобных этому:

  Ta  Tb
 |   |
 B   C
   /
   A
  

Ta и Tb — это два разных класса шаблонов, которые оба объявляют виртуальную функцию с именем f(). Я хочу переопределить две функции в области A, потому что мне приходится взаимодействовать с данными как B, так и C в этих методах. Но я не знаю, как это сделать.

 class Tb {
protected:
    virtual void f() {};
public:
    void call() {
        this->f();
    };  
};

class Tc {
protected:
    virtual void f() {};
public:
    void call() {
        this->f();
    };
};

class B : public Tb {
public:
    void doSomething() {};
};

class C : public Tc {
private:
    int c;
public:
    void inc() { c  ; };
};

class A : public B, public C {
protected:
    void f() { // this is legal but I don't want to override both with the same definition.
        // code here
    }
    // if Tb::f() is called then i want to call C::inc()
    // if Tc::f() is called then i want to call B::doSomething()
public:
    void call() {
        B::call();
        C::call();
    };
};
  

Существует ли синтаксис для переопределения обоих методов с разными определениями или я должен определить их в B и C?

Спасибо

Редактировать: Моя проблема не в том, что не удается вызвать Tb:: f () или Tc:: f (), а в том, что я хочу определить два разных поведения, если вызывается Tb:: f () или Tc:: f(). Эти методы вызываются Tb и самим Tc в своих собственных общедоступных методах. Изменил пример, чтобы, возможно, было более понятно, что я хочу сделать…

Ответ №1:

Существует ли синтаксис для переопределения обоих методов с разными определениями или я должен определить их в B и C?

Короткий ответ: нет.

Ваш A:f() переопределит оба Ta::f() и Tb::f() .

Если вам нужны разные переопределения, единственное решение — вставить вспомогательные классы Hb и Hc:

 Ta  Tb
|   |
B   C
|   |
Hb  Hc
  /
  A 
  

Hb может быть всего две функции, просто переименуйте функцию:

 class Hb: public B {
   protected: // overrides from Ta
     virtual void f() { Ta_f(); }

   protected: // new virtuals
     virtual void Ta_f() = 0;
 };
  

и тогда вы можете определить, A::Ta_f() чтобы делать все, что вы хотите (включая вызов B::f() !)

Комментарии:

1. Спасибо за совет, я имел в виду это решение, но поскольку оно добавляет слой, я отказался от его использования и поискал другое решение. Но их нет, поэтому я использую его.

2. @Gianni Технически, нет никаких препятствий для того, чтобы делать то, что вы хотите. В C просто нет синтаксиса для этого. (Например, C # имеет синтаксис, позволяющий делать именно то, что вы хотели)

3. В C # вообще нет множественного наследования.

4. @Vlad C # имеет множественное наследование, но оно ограничено только интерфейсами. В результате программисту не нужно знать об этом. Тем не менее, за кулисами компилятору приходится решать большинство проблем, возникающих при реализации множественного наследования (например, функции trampoline, которые регулируют указатель this / self).

Ответ №2:

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

Вы можете переопределить эти методы отдельно в B и C классах, но опять же, как только A класс наследует оба, вы должны решить, какой из них следует использовать.

Комментарии:

1. не могли бы вы выполнить отправку из A:: f, вызвав b:: f () или c:: f () ?

2. На самом деле это не дает ответа на вопрос (как по-другому переопределить методы), не так ли?

3. @Mario: Ты мог бы. Но проще сказать using b::f , а не писать фиктивную реализацию, которая вызывает один из методов базового класса.

Ответ №3:

Обязательно ли вам иметь возможность заменять A либо на Tb , либо на Tc a? Если нет, рассматривали ли вы возможность использования композиции в A вместо наследования?

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

Комментарии:

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