2015-09-25 2 views
2

можно ли наследовать реализацию интерфейса без его реализации для каждой реализации?Возможно ли наследовать реализацию интерфейса

enter image description here

В изображении выше IBase и Ichild классы, имеющие только чистые виртуальные методы и Base реализует способы IBase и Child реализует методы IChild. Возможно ли, чтобы Child наследовал реализации методов IBase через Base, так что Child не должен реализовывать эти методы самостоятельно?

Я пытался реализовать это следующим образом:

struct IBase { 
    virtual void do_something_Base() = 0; 
}; 

struct IChild : public virtual IBase { 
    virtual void do_something_Child() = 0; 
}; 


struct Base : public virtual IBase { 
    virtual void do_something_Base() { //implementation IBase method 
     //do sth 
    } 
}; 

struct Child : public Base , public IChild { 
    virtual void do_something_Child() { //implementation IChild method 
     //do sth 
    } 
}; 

int main() { 

    IBase* B = new Child; 
    B->do_something_Base(); 

    delete B; 

    return 0; 
} 

Проблема с этим кодом является то, что copiler выдает предупреждение:

предупреждение C4250: 'Child': наследуется «Base :: Base :: do_something_Base 'через доминирование

Futhermore программа вылетает по линии delete B. (Если я использую IChild* B вместо IBase* B программы не врезаться. Почему?)

Заранее спасибо

+0

https://en.wikipedia.org/wiki/Virtual_inheritance –

+0

Да, я знаю виртуальное наследование, но там компилятор дает предупреждение. В предупреждении говорится, что методы «IBase» наследуются доминированием. – Philinator

+1

Если у вас есть ошибка кода, пожалуйста, ** ** отредактируйте свой вопрос с помощью [mcve] или [SSCCE (Short, Self Contained, Correct Example)] (http://sscce.org) – NathanOliver

ответ

2

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

  1. Дайте IBase виртуальный деструктор. Виртуальный деструктор необходим за возможность удаления с помощью указателей в базовый класс.

  2. Чтобы быть в безопасности, выведите Child от IChild практически так же. Так как классы I не имеют данных, нет смысла использовать их как , а не - виртуальные базы.

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

+0

Что делает виртуальное наследование 'IChild'? – Philinator

+0

@Philinator Как и любое другое виртуальное наследование - гарантирует, что в самом производном объекте есть только один подобъект этого типа, даже если из него происходит более одного класса. Поскольку у вас есть это, у вас будет один «IChild», присутствующий в подобъекте «Base» (фактически полученный), * и * один 'IChild', присутствующий непосредственно в' Child' (тот, который был получен нормально). – Angew

+0

Спасибо за ваше объяснение – Philinator

1

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

Но авария при удалении связана с тем, что указатель на объект через один класс и производный не должен иметь одинакового представления (*). И вы забыли добавить виртуальный деструктор в IBase и IChild, в то время как он настоятельно рекомендуется для наследуемых классов и необходимо разрешить удаление с помощью указателя на унаследованный класс, как указано в @Angew.

Если у вас есть веские причины не иметь виртуального деструктора (даже если я не могу представить его), вы должны отправить его на Child * перед вызовом delete на нем.Кроме того, как IBase является виртуальным, вы должны использовать dynamic_cast:

delete dynamic_cast<Child>(B); 

Альтернатива, чтобы сохранить оригинальный указатель, который был возвращен через новые и удалить его вместо того, чтобы использовать преобразование в IBase*:

Child* C = new Child; 
IBase *B = C; 

B->do_something_Base(); 

delete C; // deleting original pointer is safe 

(*) просто чтобы быть уверенным, просто добавьте в код:

std::cout << "IBase*:" << B << " Child*:" << dynamic_cast<Child>(B) << std::endl; 
+0

Это не работает для меня, потому что я положил 'Base' и' Child' в dll, и они не видны снаружи – Philinator

+0

Да, кастинг это сработает, но это очень громоздко. Добавление виртуального dtor в 'IBase' является правильным решением. – Angew

+0

@ Ангел: Конечно! Я отредактировал свой пост, соответственно, предоставив вам кредит, но я оставляю его здесь, потому что кастинг может быть вариантом. –