Операторы неявного преобразования C ++ - PullRequest
6 голосов
/ 20 марта 2010

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

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

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

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

bool IsPointInRectangle(const Rectangle& rect);

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

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

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

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

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

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

Спасибо.

Ответы [ 3 ]

6 голосов
/ 20 марта 2010

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

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

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

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

3 голосов
/ 20 марта 2010

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

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

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

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

0 голосов
/ 20 марта 2010

Я полагаю, хотя я не уверен, что вам нужно использовать явное приведение, чтобы вызвать этот оператор преобразования в этом контексте. База 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); }

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