Операторы с неявным преобразованием C ++

Я пытаюсь найти хорошее решение наследования в C ++.

У меня есть class Rectangle и квадратный class. Класс Square не может публично наследоваться от Rectangle, поскольку он не может полностью выполнить требования прямоугольника. Например, прямоугольник может иметь ширину и высоту каждого из них отдельно, и это, конечно, невозможно с квадратом.

Итак, моя дилемма. Квадрат, очевидно, будет делиться большим количеством кода с Rectangle; они очень похожи.

Например, если у меня есть такая функция, как:

bool IsPointInRectangle(const Rectangle& rect); 

он должен работать и на квадрате. На самом деле, у меня есть тонна таких функций.

Поэтому, создав class Square, я решил, что буду использовать частное наследование с публично доступным оператором преобразования Rectangle. Поэтому мой квадратный class выглядит так:

 class Square : private Rectangle { public: operator const Rectangle&() const; }; 

Однако, когда я пытаюсь передать квадрат функции IsPointInRectangle, мой компилятор просто жалуется, что «Rectangle – недоступная база» в этом контексте. Я ожидаю, что он заметит оператор Rectangle и будет использовать его вместо этого.

Является ли то, что я пытаюсь сделать, даже возможно?

Если это не сработает, я, вероятно, собираюсь реорганизовать часть Rectangle в class MutableRectangle .

Благодарю.

Ну, я удивлен. Кажется, что частное наследование classа A не позволяет вам использовать оператор A вне classа.

Вы можете решить свою проблему, сделав элемент Rectangle для квадрата и используя его для трансляции:

 class Square { Rectangle r; public: operator const Rectangle&() const { return r; } }; 

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

Вы можете создать class ImmutableRectangle без каких-либо мутаторов и только с помощью методов const , из которых вы можете правильно выводить как Rectangle , так и отдельно ImmutableSquare и, от этого, Square . Обратите внимание, что с измененной изменчивостью отношения IS-A действительно сохраняются – непреложный прямоугольный прямоугольник IS-A: изменчивость является единственной серьезной проблемой, поэтому, разлагая ее, вы можете получить некоторое существенное повторное использование кода (для всех использования const – – те, которые на самом деле не используют или не нуждаются в изменчивости).

Введение изменчивости по наследованию в порядке, если никакие инварианты classа (неизменяемой) базы фактически не зависят от характеристики непреложности; и, конечно, неизменяемый объект может быть правильно построен из указателя const или ссылки на изменяемую версию (предположительно в отдельной встроенной функции друга, чтобы избежать предоставления базовому classу зависимости от производного classа 😉 для разумного использования.

Изменить : один комментарий по-видимому выражает угрызения совести, потому что «мутабе не является непреложным»: чтобы рассуждать об этом, вам нужно понять, что означает «IS-A» … и это не значит, что Коржибский is «идентичность», : это означает LSP . Пройдите через rigmarole ограничений, это означает: ковариация, контравариантность, более слабые равные предпосылки, более сильные постусловия и т. Д., Поскольку они применимы к const- методам базовых (неизменяемых) и производных (изменяемых) classов. Вы увидите, что инварианты classа являются единственной проблемой, как я упоминал в предыдущем абзаце, поэтому просто избегайте утверждать неизменность как инвариант classа, а вы в клевере ;-).

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

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

 /** * Base for rectangular classes; name it whatever you want. */ class RectangularBase { public: virtual unsigned int getValue(int) const = 0; }; /** * Base for specific rectangular classes; also named whatever. */ template class Rectangular : public RectangularBase { public: virtual unsigned int getValue(int index) const { return values[index]; } private: unsigned int values[Dimensions]; }; /** * A square is Rectangular but has only one significant dimension. */ class Square : public Rectangular<1> { public: unsigned int getSideLength() const { return getValue(0); } }; /** * A Rectangle is Rectangular and has two significant dimensions. */ class Rectangle : public Rectangular<2> { public: unsigned int getWidth() const { return getValue(0); } unsigned int getHeight() const { return getValue(1); } };