2015-03-26 5 views
1

Вопросс ++ реализация абстрактного класса в другой базовый класс

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

Брифинг:

В процессе обучения C++, я пришел с некоторыми ошибками компилятора для следующего кода:

#include <stdlib.h> 
#include <iostream> 

class IFoo{ 
    public: 
    virtual void Hello() = 0; 
}; 

class Foo : public IFoo{ 
    public: 
    void Hello() 
    { 
     printf("Hello"); 
    } 
}; 

class IBar : public IFoo{ 
    public: 
    virtual void HelloWorld() = 0; 
}; 

class Bar : public IBar, public Foo{ 
    public: 
    void HelloWorld() 
    { 
     Hello(); 
     printf("World"); 
    } 
}; 

int main() 
{ 
    Bar b; 
    b.HelloWorld(); 

    system("pause"); 
    return 0; 
} 

Я хочу Бар реализовать абстрактный класс IBar, который имеет IFoo в качестве базового класса, но то я хочу, чтобы вся реализация IFoo предоставлялась вторым базовым классом Bar Foo.

Я получаю 2 ошибки компилятора (GCC 4.9.2): Один из них связан с двусмысленностями, а другой - с отсутствующими реализациями для абстрактного класса IFoo.

Затем я нашел this question и получил, чтобы встретить концепцию виртуального наследования, которая приведет меня к this page. После урока я добавил виртуальное наследование и все проблемы исчезли:

class Foo : public virtual IFoo{... 
class IBar : public virtual IFoo{... 

Но пользователь в question предложить не попробовать виртуальное наследование. Отсюда мой вопрос.

ответ

0

Ваш Hello() звонок в Bar::HelloWorld() неоднозначен. Компилятор не может сказать, пытаетесь ли вы позвонить Hello() с IBar или Foo. Вы можете позвонить Foo::Hello() или IBar::Hello() в свое место. При этом ваша структура наследования не имеет смысла.

Ваш класс Bar также должен переопределить Hello, что он наследует от IBar.

Если у вас есть C++ 11, было бы хорошей идеей, чтобы отметить виртуальные функции вы переопределение с override ключевым словом:

#include <stdlib.h> 
#include <iostream> 

class IFoo{ 
    public: 
    virtual void Hello() = 0; 
}; 

class Foo : public IFoo{ 
    public: 
    virtual void Hello() override 
    { 
     printf("Hello"); 
    } 
}; 

class IBar : public IFoo{ 
    public: 
    virtual void HelloWorld() = 0; 
}; 

class Bar : public IBar, public Foo{ 
    public: 
    virtual void HelloWorld() override 
    { 
     Foo::Hello(); 
     printf("World"); 
    } 

    void Hello() override {} 
}; 

int main() 
{ 
    Bar b; 
    b.HelloWorld(); 

    system("pause"); 
    return 0; 
} 

Чтобы ответить на ваш вопрос о виртуальном наследовании, вы могли бы легко изменить ваша структура наследования к чему-то, что все еще работает, что позволяет избежать алмазной модели. Ромбовидная редко требуется, и, как правило, можно избежать:

#include <stdlib.h> 
#include <iostream> 

class IFoo{ 
public: 
    virtual void Hello() = 0; 
}; 

class Foo : public IFoo{ 
public: 
    virtual void Hello() override 
    { 
     printf("Hello"); 
    } 
}; 

class IBar { 
public: 
    virtual void World() = 0; 
}; 

class Bar : public IBar{ 
public: 
    virtual void World() override 
    { 
     printf("World"); 
    } 
}; 

class FooBar : public Foo, public Bar 
{ 
public: 
    virtual void HelloWorld() 
    { 
     Hello(); 
     World(); 
    } 
}; 

int main() 
{ 
    FooBar b; 
    b.HelloWorld(); 

    system("pause"); 
    return 0; 
} 
+0

Вы правы. Я попробовал Foo :: Hello() в Bar :: HelloWorld() без виртуального наследования и избавился от двусмысленности. Я также участвую в C++ 11, поэтому я буду применять переопределяющую практику. Эта структура наследования может не иметь смысла сейчас, но если IFoo имеет 1000+ определений функций, было бы действительно утомительно переопределить все из них в Bar с функциями формы Bar :: func1000() override {Foo :: func1000(); }. Вот почему я ожидал, что Foo предоставит реализацию, но это работает только с виртуальным наследованием. – Heinz

+0

@Heinz Вопрос в том, почему у вас есть бар, наследуемый как от «Foo», так и от «IBar»? Вы в конечном итоге наследуете от 'Foo' дважды, что и вызывает проблемы. Потребность в виртуальном наследовании чаще всего означает плохой дизайн. Для этого определенно используется, но здесь это не имеет смысла. Кроме того, если на ваш вопрос был дан ответ, вы должны отметить один из ответов, как принято. – dwcanillas

+0

@Heinz [Проверить это] (http://www.cprogramming.com/tutorial/virtual_inheritance.html). – dwcanillas

0

Ваш класс

Bar 

необходимо переопределить

Hello 

иначе класс Bar также становится чисто абстрактным, потому что

Hello 

наследуется весь путь от

IFoo. 

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

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

// this class is abstract, it doesn't implement Base::foo 
class Derived1 : public Base{ 
public: 
    void fooDerived() {} 
}; 

// this class is not abstract, it implements Base::foo 
class Derived2 : public Base{ 
public: 
    void foo() {} 
}; 

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

// this class is not abstract, it implements Base::foo 
class Derived2 : public Base{ 
public: 
    void foo() override {} 
}; 

Виртуальное наследование решает другую проблему, проблема неоднозначности при наследовании от более чем одного базового класса, который декларирует одинаковые функции или членов.

+0

В этом случае я могу легко переопределить Hello() в Bar с помощью Bar :: Hello() override {Foo :: Hello(); }, чтобы дать ему хотя бы некоторые функциональные возможности, но представьте себе, как это сделать для каждой функции IFoo, возможно, в будущем (1000+ будет преувеличено), мне нужно будет поддерживать IFoo, Foo и теперь Bar ... виртуальное наследование помогло решить это, используя Foo, но до сих пор не знаю, почему предлагается не использовать его. – Heinz

+0

Если у вас возникли проблемы с сохранением структуры наследования между IFoo, Foo и Bar, это проблема дизайна. Виртуальные функции и абстрактные базовые классы действительно служат в объектно-ориентированном программировании. Я бы назвал абсолюты, такие как «не использовать виртуальное наследование». – jensa

+0

Я думаю, что это скорее проблема ленивости :), но конечно, у меня нет серьезных проблем с применением дизайна без виртуального наследования, переопределив Hello in Bar и вызывая Foo :: Hello() в Bar :: HelloWorld(), если это необходимо сделайте, тогда давайте сделаем это, это, кажется, работает просто отлично. Я просто не знаю, почему виртуальное наследование не является лучшим выбором, например, имеет ли это ограничение производительности? – Heinz