Частное наследование всегда означает "HAS-A"?
Согласно моему любимому автору, г-ну Скотту Мейерсу, частное наследство и композиция означают то же самое, что и отношения Has-A. Таким образом, все, что может быть получено из состава (сдерживание, когда класс А имеет класс В в качестве его члена), может быть получено частным наследством и наоборот.
Таким образом, следующий код должен быть отношением Has-A, но, с моей точки зрения, его нет!
class A : private boost::noncopyable {.. this is irrelevant };
Может ли кто-нибудь сказать мне, что я пропал? Или как этот код может быть реализован посредством композиции?
Ответы
Ответ 1
Пример можно реализовать с помощью композиции следующим образом:
class A {
private:
class B {
B(const B&) = delete;
B& operator=(const B&) = delete;
} b;
};
A
является не подлежащим копированию, поскольку его член b
не поддается копированию.
Ответ 2
Ваш пример все еще является отношением HAS-A.
Учитывая следующий класс в качестве примера, который примерно эквивалентен boost:noncopyable
:
class X {
private:
X(const X &);
X &operator=(const X &);
public:
X() {}
};
Следующие два класса имеют одинаковую функциональность:
class A : private X {
public:
int a;
char b;
};
class B {
public:
int a;
char b;
X x;
};
В этом примере A
наследует конфиденциально от X
а B
содержит экземпляр X
A
нельзя скопировать, поскольку он не может вызвать конструктор родительских копий, а B
не может быть скопирован, потому что он не может вызвать конструктор копирования одного из его членов.
A a1;
A a2(a1); // error
B b1;
B b2(b1); // error
Ответ 3
boost::noncopyable
не имеет реального смыслового смысла, это всего лишь деталь реализации, чтобы запретить внукам.
class A : private boost::noncopyable {};
A
не имеет boost::noncopyable
, так как boost::noncopyable
пуст, как в буквальном смысле, так и в значении. Здесь вы могли бы сказать: " A
не подлежит копированию", но я в общем согласен с точкой зрения Мейерса в общем случае.
Ответ 4
На этот вопрос можно ответить таким образом, чтобы избежать специфики обсуждения конкретного примера. Класс, который наследует публично, начинается со всего, что определяет его родительскую семантику - ее публичные функции, а также переменные public state, если они есть. Если ни одно из них не отменено, оно соответствует принципу замещения Лискова, и это общепринятый принцип проектирования, который переопределяет эти свойства таким образом, чтобы сохранить замещаемость.
При частном наследовании ни одно из этого не применяется, если программист не захочет реализовать (или повторно реализовать) в производном классе все общедоступные свойства родительского класса таким образом, чтобы сохранить замену. Поскольку C++ не требует, чтобы частный класс применял версии своих родительских общедоступных методов и переменных, это ничем не отличается (кроме незначительных и изменчивых изменений в коде), чем если бы производный класс вместо этого содержал экземпляр родительского класс как частный член. В частности, с частным наследованием производный класс не является функциональным или операционным способом подтипом родительского типа, и если ваш язык рассматривает производные классы, как если бы они были подтипами, была возможность недопонимания и путаницы (хотя следует отметить, что, если у вашего языка есть способ обеспечить семантическую достоверность подтипов (которые C++ не делает), тогда это фактически вопрос стиля.)