Реализация абстрактных членов класса в родительском классе - PullRequest
3 голосов
/ 24 августа 2011

Можно ли реализовать абстрактный базовый класс с членами, унаследованными от другого родительского класса в C ++?

Он работает в C #, поэтому я попытался сделать это в C ++:

// Virtual destructors omitted for brevity

class ITalk
{
public:
    virtual void SayHi() = 0;
};

class Parent
{
public:
    void SayHi();
};

class Child : public Parent, public ITalk
{
};

void Parent::SayHi()
{
    std::printf("Hi\n");
}

Моему компилятору это не очень понравилось:

ITalk* lChild = new Child(); // You idiot, Child is an abstract class!
lChild->SayHi();

Я не могу добавить public ITalk в класс Parent, потому что "базовый класс«ITalk» уже является базовым классом «Parent». "Я мог бы переместиться public ITalk в класс Parent, но в моем конкретном сценарии это сильно усложняет.

Ответы [ 4 ]

4 голосов
/ 24 августа 2011

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

Italk    Parent
 / \       / \
  |         |
  +---------+
       |
     Child

Если бы Parent и Italk имели две переменные с именем i, было бы два экземпляра "i", ITalk :: i и Parent :: i.Чтобы получить к ним доступ, вам нужно полностью указать, какой из них вы хотите.

То же самое относится и к методам, в lChild есть два метода с именем SayHi, и вам необходимо уточнить, какой из них вы имеете в виду при вызове SayHi, поскольку множественное наследованиесделал это неоднозначным.

У вас есть SayHi Родителя

lChild->Parent::SayHi();

и SayHi Италка:

lChild->ITalk::SayHi(); 

Последний является чисто виртуальным, и его абстракция должна быть переопределена локально в Child.Для этого вам нужно определить

Child::SayHi();

, который теперь будет скрывать Parent :: SayHi () при вызове SayHi, не ограничивая его классом:

lChild->SayHi() //parent's now hidden, invoke child's

Конечно Child:: SayHi () может вызвать Parent :: SayHi ():

void Child::SayHi()
{
     Parent::SayHi();
}

, что решит вашу проблему.

1 голос
/ 24 августа 2011

Невозможно сделать так, как ты это написал. Причина этого в том, что каждому нестатическому методу нужен объект (this) для работы (здесь вы не используете какие-либо поля или методы объекта, но это не имеет значения), и этот объект должен быть подходящего типа. Parent::sayHi ожидает, что this будет иметь тип Parent, и поскольку ITalk вообще не относится к Parent, методы Parent::sayHi и ITalk::sayHi принципиально несовместимы.

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

В C ++ самым простым способом реализации такого поведения было бы просто заставить Child::sayHi вызвать Parent::sayHi, поскольку Child - единственный класс, который "знает", где находятся Parent и ITalk и как они должны быть связаны между собой.

class Child : public Parent, public ITalk
{
    virtual void sayHi(){ Parent::sayHi(); }
};
1 голос
/ 24 августа 2011

ITalk содержит чисто виртуальную функцию SayHi(), поэтому, если вы хотите иметь возможность создавать экземпляр класса, производного от ITalk, этот класс должен реализовывать SayHi().

class Child : public Parent, public ITalk
{
public:
  void SayHi() { std::cout << "Hi!" << std::endl; }
};

В качестве альтернативы, Parent может наследоваться от ITalk (но не Child), и опубликованный вами код будет работать.

Кроме того, при реализации базового класса с виртуальными функциями вы должны определить виртуальные деструкторы для этих базовых классов.Итак, ITalk должен быть:

class ITalk
{
public:
    virtual void SayHi() = 0;
    virtual ~ITalk() {}
};

Если вы этого не сделаете, следующее приведет к неопределенному поведению

ITalk* lChild = new Child();
lChild->SayHi();
delete lChild;
0 голосов
/ 24 августа 2011

Попробуйте использовать виртуальное наследование

class ITalk
{
public:
  virtual void SayHi() = 0;
};

class Parent: virtual ITalk
{
public:
   void SayHi();
};

class Child : public Parent, public virtual ITalk
{
};

void Parent::SayHi()
{
    std::printf("Hi\n");
}
...