Я пытаюсь найти хорошее решение для наследования в C++.
У меня есть Прямоугольный класс и Квадратный класс. Квадратный класс не может публично наследоваться Прямоугольнику, потому что он не может полностью выполнить требования прямоугольника. Например, Прямоугольник может иметь, это - ширина и высота каждый набор отдельно, и это, конечно, невозможно с Квадратом.
Так, моя дилемма. Квадрат, очевидно, совместно использует много кода с Прямоугольником; они весьма схожи.
Для примера, если у меня есть функция как:
bool IsPointInRectangle(const Rectangle& rect);
это должно работать на квадрат также. На самом деле у меня есть тонна таких функций.
Таким образом в создании моего Квадратного класса, я полагал, что буду использовать частное наследование с публично доступным Прямоугольным оператором преобразования. Таким образом, мой квадратный класс похож:
class Square : private Rectangle
{
public:
operator const Rectangle&() const;
};
Однако, когда я пытаюсь передать Квадрат функции IsPointInRectangle, мой компилятор просто жалуется, что "Прямоугольник является недоступной основой" в том контексте. Я ожидаю, что это заметит оператор Rectangle и использование это вместо этого.
То, что я пытаюсь сделать даже возможный?
Если это не может работать, я, вероятно, собираюсь осуществить рефакторинг часть Прямоугольника в класс MutableRectangle.
Спасибо.
Что ж, я удивлен. Кажется, частное наследование класса A не позволяет вам использовать оператор A вне класса.
Вы можете решить вашу проблему, сделав элемент Rectangle вместо квадрата и используя его для приведения:
class Square {
Rectangle r;
public:
operator const Rectangle&() const {
return r;
}
};
Это должно скомпилироваться и работать. И я считаю, что это не даст вам столько работы, если таковая будет.
Вы можете создать класс ImmutableRectangle
без каких-либо мутаторов и с помощью только методов const
, из которых можно правильно получить как Rectangle
, так и отдельно, ImmutableSquare
и, следовательно, Square
. Обратите внимание, что, исключая изменчивость, отношение IS-A
выполняется - неизменный квадрат. Неизменяемый прямоугольник IS-A: изменчивость - единственная серьезная проблема, поэтому, вычленив ее, вы может получить существенное повторное использование кода (для всех const
применений - тех, которые на самом деле не используют или не нуждаются в изменчивости).
Введение изменчивости в процессе наследования - это нормально, пока никакие инварианты класса (неизменяемой) базы на самом деле не полагаются на характеристику неизменяемости; и, конечно же, неизменяемый объект может быть правильно сконструирован из указателя const
или ссылки на изменяемую версию (предположительно в отдельной встроенной функции друга, чтобы избежать зависимости базового класса от производного класса ;-) для разумно-удобное использование.
Править : один комментарий по понятным причинам выражает сомнения, потому что «мутабе не является неизменным»: чтобы рассуждать об этом, вам нужно понять, что означает «IS-A» ... и это не означает, что Коржибски -отрицание « есть
идентичности»: это означает LSP .Пройдите через эту банальность ограничений, это означает: ковариантность, контравариантность, предварительные условия более слабого равенства, постусловия более сильного равенства и т. Д. применительно к методам const базы (неизменяемые) и производные (изменяемые) классы. Вы увидите, что инварианты классов - единственная проблема, как я упоминал в предыдущем абзаце, поэтому просто избегайте утверждения неизменяемости как инварианта класса, и вы в клевере ;-).
Возможно, было бы полезно назвать базовый класс NotNecessuallyMutableRectangle
, поскольку он не утверждает неизменяемость как инвариант класса; это очень точное наименование может быть философски обнадеживающим, но, возможно, немного бесполезным в повседневном кодировании.
Я считаю, хотя и не уверен, что вы должны использовать явное приведение, чтобы вызвать этот оператор преобразования в этом контексте. База 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<unsigned int Dimensions>
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); }
};