2012-07-02 2 views
1

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

class Base { 
    public: 
    void privateWork(); 
    // No virtual member functions here. 
}; 

class Derived : Base { 
    public: 
    void doSomething() { 
     privateWork(); 
    } 
}; 

class Encapsulate { 
    Base memberVariable; 

    public: 
    void doSomething() { 
     memberVariable.privateWork() 
    } 
}; 
+2

Бросить GoF Design Шаблоны из окна и начать кодирование ... :) – Gigi

+0

Разве у вас нет хотя бы виртуального деструктора? –

+0

@LuchianGrigore: Если наследование является приватным, вы никак не можете уничтожить полученный из базового указателя. Поэтому я бы сказал не обязательно. –

ответ

2

Помните, что модели наследования «Liskov substitution»: Foo является баром тогда и только тогда, когда вы можете передать переменную Foo каждой функции, ожидающей бара. Частное наследование не моделирует это. Он моделирует композицию (Foo реализуется в терминах Bar).

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

Однако, иногда, частное наследование удобно:

class FooCollection : std::vector<Foo> 
{ 
public: 
    FooCollection(size_t n) : std::vector<Foo>(n) {}; 

    using std::vector<Foo>::begin; 
    using std::vector<Foo>::end; 
    using std::vector<Foo>::operator[]; 
}; 

Это позволяет повторно использовать некоторые из функциональных vector без необходимости пересылать вручную 2 версии (сопзЬ + нон сопзЬ) от начала, конца, и operator[].

В этом случае у вас вообще нет полиморфизма: это не наследование, это состав маскировки; вы не можете использовать FooCollection в качестве вектора. В частности, вам не нужен виртуальный деструктор.

2

Если нет virtual функции, то наследование не следует использовать в ОО. Обратите внимание: это не означает, что его нельзя использовать, есть несколько (ограниченных) случаев, когда вам может понадобиться (ab) использовать наследование для других целей, кроме OO.

+0

Иногда личное наследование отлично подходит для «реализованных в терминах» отношений, которые я считаю. –

+0

@MarkB: наследование является самой распространенной функцией. Как правило, наследование заключается в повторном использовании кода, но в противоположном направлении, которое считает большинство людей: Наследование должно использоваться так, чтобы код, который работает с вашей базой, будет работать с вашим новым типом (в отличие от злоупотребления наследованием для повторного использования функциональности в вашей базе). Класс, не имеющий виртуальных функций (размахивание рукой: т.е. без статического полиморфизма), не должен быть выведен: существующий код, который принимает объект «Base», не сможет делать новые вещи, беря ссылку «Derived» –

+0

@MarkB: Рассмотрим которая имеет класс таймера, на который вы можете зарегистрироваться, чтобы получать уведомления. Для фреймворка требуется определенный интерфейс для регистрации в таймере (например, «Уведомление»). Теперь подумайте, что вы хотите предлагать периодический таймер в своем приложении, для которого точная фреймворк используется только как деталь реализации. Затем вы должны использовать частное наследование для «реализованных в терминах», то есть: с точки зрения клиента ваш класс не является экземпляром «Notifiable», а внутренне - как вы реализуетесь. –

Смежные вопросы