Частное наследование всегда означает "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++ не делает), тогда это фактически вопрос стиля.)