Jste zde

Zajímavé úlohy pro programátory v C a C++ #8

Přínáším první hádanku v novém školním roce. Styl této hádanky bude podobný stylu předchozí hádanky, kdy se hledala potenciální chyba v programu, ovšem s tím rozdílem, že tato hádanka nebude ani tak o hledání "jazykových chyb", jako spíše o hledání návrhových chyb.

Zadání

Níže uvedené příklady jsou zapsány podle normy ISO C++98. Představte si, že máte nadefinovanou následující třídu reprezentující bod ve dvourozměrném prostoru (pro nás zbytečné metody jsou vynechány).

class Point {
    private:
        int x, y;
 
    public:
        Point(int x, int y): x(x), y(y) { }
        virtual ~Point() { }
 
        // Kopirovaci konstruktor, operator=(), getX(), getY() ...
 
        virtual bool operator==(const Point &other) const {
            return x == other.x && y == other.y;
        }
 
        virtual bool operator!=(const Point &other) const {
            return !(*this == other);
        }
};

Body se porovnávají na základě jejich pozice v prostoru. Později se rozhodnete nadefinovat třídu, která bude reprezentovat bod ve dvourozměrném prostoru, který je navíc barevný (ano, bod má matematicky nulovou velikost, ale předpokládejme, že když se zobrazí, tak barva bude viditelná). Využijeme naši třídu Point a vytvoříme poddtřídu ColoredPoint.

#include <typeinfo>
 
using namespace std;
 
class ColoredPoint: public Point {
    public:
        enum Color { RED, GREEN, BLUE };
 
    private:
        Color color;
 
    public:
        ColoredPoint(int x, int y, Color color): Point(x, y), color(color) { }
 
        // Kopirovaci konstruktor, operator=(), getColor(), ...
 
        virtual bool operator==(const Point &other) const {
            // Porovname body metodou v nadtride
            bool pointsEqual = Point::operator==(other);
 
            // Pokud se jedna o barevne body, porovname i barvu
            if (typeid(other) == typeid(ColoredPoint)) {
                pointsEqual = pointsEqual && color == static_cast<const ColoredPoint &>(other).color;
            }
 
            return pointsEqual;
        }
};

Operaci porovnání jsme nadefinovali tak, že pokud porovnáváme dva body (nebarevné), tak porovnáváme pouze jejich pozici, jinak porovnáme i barvu. Otázka je následující: jakým problémem toto řešení trpí a (doplňující otázka) jak ho napravit?

Řešení

Z pohledu matematiky je relace rovnosti relací reflexivní (obj == obj), symetrickou (obj1 == obj2 => obj2 == obj1) a tranzitivní (obj1 == obj2 and obj2 == obj3 => obj1 == obj3). Výše uvedená implementace splňuje reflexivitu a symetričnost, ale už ne tranzitivitu. Vezměme následující příklad:

int main() {
    ColoredPoint cp1(1, 1, ColoredPoint::RED);
    ColoredPoint cp2(1, 1, ColoredPoint::GREEN);
    Point p(1, 1);
 
    assert(cp1 == p);
    assert(p == cp2);
    assert(cp1 == cp2);
}

Pokud tento program přeložíme a spustíme, skončíme na neplatnosti asserce (je jasné, že červený bod není to samé, jako zelený bod):

int main(): Assertion `cp1 == cp2' failed.

A jak to tedy vyřešit? Výše uvedeným způsobem to opravdu nepůjde, takže je třeba vždy porovnávat body s body a barevné body s barevnými body. To znamená, že metoda ColoredPoint::operator== by měla brát parametr typu const ColoredPoint & a ve třídě ColoredPoint poskytněte následující metodu náhledu:

    virtual Point asPoint() const {
        return Point(*this);
    }

Komentáře

Zveřejnil jsem řešení osmého úkolu.

Přidat komentář