Когда я должен использовать частное наследование C ++? - PullRequest
108 голосов
/ 18 марта 2009

В отличие от защищенного наследования, частное наследование C ++ нашло свое отражение в основной разработке C ++. Тем не менее, я до сих пор не нашел хорошего использования для этого.

Когда вы, ребята, используете это?

Ответы [ 13 ]

0 голосов
/ 13 апреля 2017

Класс содержит инвариант. Инвариант устанавливается конструктором. Однако во многих ситуациях полезно иметь представление о состоянии представления объекта (которое вы можете передать по сети или сохранить в файл - DTO, если хотите). REST лучше всего делать с точки зрения AggregateType. Это особенно верно, если вы правы. Рассмотрим:

struct QuadraticEquationState {
   const double a;
   const double b;
   const double c;

   // named ctors so aggregate construction is available,
   // which is the default usage pattern
   // add your favourite ctors - throwing, try, cps
   static QuadraticEquationState read(std::istream& is);
   static std::optional<QuadraticEquationState> try_read(std::istream& is);

   template<typename Then, typename Else>
   static std::common_type<
             decltype(std::declval<Then>()(std::declval<QuadraticEquationState>()),
             decltype(std::declval<Else>()())>::type // this is just then(qes) or els(qes)
   if_read(std::istream& is, Then then, Else els);
};

// this works with QuadraticEquation as well by default
std::ostream& operator<<(std::ostream& os, const QuadraticEquationState& qes);

// no operator>> as we're const correct.
// we _might_ (not necessarily want) operator>> for optional<qes>
std::istream& operator>>(std::istream& is, std::optional<QuadraticEquationState>);

struct QuadraticEquationCache {
   mutable std::optional<double> determinant_cache;
   mutable std::optional<double> x1_cache;
   mutable std::optional<double> x2_cache;
   mutable std::optional<double> sum_of_x12_cache;
};

class QuadraticEquation : public QuadraticEquationState, // private if base is non-const
                          private QuadraticEquationCache {
public:
   QuadraticEquation(QuadraticEquationState); // in general, might throw
   QuadraticEquation(const double a, const double b, const double c);
   QuadraticEquation(const std::string& str);
   QuadraticEquation(const ExpressionTree& str); // might throw
}

На этом этапе вы можете просто хранить коллекции кеша в контейнерах и искать их при создании. Удобно, если есть реальная обработка. Обратите внимание, что кэш является частью QE: операции, определенные в QE, могут означать, что кэш частично используется повторно (например, c не влияет на сумму); тем не менее, когда кеша нет, стоит поискать его.

Частное наследование может почти всегда моделироваться участником (при необходимости сохраняя ссылку на базу). Просто не всегда стоит так моделировать; иногда наследование является наиболее эффективным представлением.

0 голосов
/ 24 ноября 2015

Частное наследование, которое будет использоваться, когда отношение не является «является», но Новый класс может быть «реализован в терминах существующего класса» или новый класс «работает как» существующего класса.

пример из "Стандартов кодирования C ++ Андрея Александреску, Херба Саттера": - Учтите, что два класса Square и Rectangle имеют виртуальные функции для установки их высоты и ширины. Тогда Square не сможет правильно наследовать от Rectangle, потому что код, который использует модифицируемый Rectangle, будет предполагать, что SetWidth не изменяет высоту (независимо от того, явно ли Rectangle документирует этот контракт или нет), тогда как Square :: SetWidth не может сохранить этот контракт и свой собственный инвариант прямоугольности в в то же время. Но Rectangle также не может правильно наследовать от Square, если клиенты Square предполагают, например, что площадь квадрата равна его ширине в квадрате, или если они полагаются на какое-то другое свойство, которое не сохраняется для прямоугольников.

Квадратный прямоугольник "is-a" (математически), но Квадрат не является прямоугольником (поведенчески). Следовательно, вместо «is-a» мы предпочитаем говорить «works-like-a» (или, если вы предпочитаете «useable-as-a»), чтобы сделать описание менее подверженным недоразумениям.

0 голосов
/ 24 марта 2009

То, что C ++ имеет функцию, не означает, что она полезна или что ее следует использовать.

Я бы сказал, что ты не должен использовать это вообще.

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

Как и другие функции C ++, его можно использовать для достижения побочных эффектов, таких как запечатывание класса (как упоминалось в ответе dribeas), но это не делает его хорошей функцией.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...