Heterogén kollekció és modern C++

Czirkos Zoltán, Dobra Gábor · 2019.02.27.

1. Kiindulási alap

A kiindulási alap egy egyszerű heterogén kollekció: Alakzat, Szakasz, Kör, Poligon stb.

A problémánk ezzel a kóddal az, hogy nagyon belefolyik az osztályokba a memóriakezelés problémája. Rengeteg a new és a delete, nekünk kell megírni a másoló konstruktorokat, destruktorokat, és ez sok hibalehetőséget hordoz.

Az első változat letölthető innen: heterogen_alakzat_eredeti.zip.

2. Módosítások

Az elvégzett módosítások az alábbiak:

  • Az összes dinamikus tömböt std::vector-ra cseréltük.

  • A poligon át tud venni konstruktorparaméterben egy pontokból álló vektort. Így annak csúcsai már a konstruktorban megadhatóak. Ugyanolyan egyszerűen példányosítható, mint az összes többi alakzat.

  • Az alakzatokhoz bevezettünk egy masol() virtuális függvényt, amely minden alakzatról a típusának megfelelő, dinamikusan foglalt másolatot készít. Ezzel lehetővé tettük a heterogén kollekció, a rajztábla másolását.

  • Az alakzatokat reprezentáló Alakzat * pointert egy osztályba csomagoltuk, amely elrejti előlünk a heterogén alakzat dinamikus memóriakezelése miatti problémákat. Ez lett az új Alakzat osztály. Lásd lentebb.

3. Végeredmény

A legfontosabb eredményünk a következő osztály:

class Alakzat {
    private:
        AlakzatOs * a;

    public:
        explicit Alakzat(AlakzatOs * a) : a(a) {}

        Alakzat(Alakzat const & masolando) {
            a = masolando.a->masol();
        }

        Alakzat& operator=(Alakzat const & masolando) {
            if (this != &masolando) {
                delete a;
                a = masolando.a->masol();
            }
            return *this;
        }

        ~Alakzat() {
            delete a;
        }

        AlakzatOs * operator->() {
            return a;
        }
        AlakzatOs const * operator->() const {
            return a;
        }
};

Ez érték szerint másolhatóvá tesz bármilyen alakzatot. A konstruktorában átvesz egy dinamikusan foglalt példányt, aztán 1) ha megszűnik, akkor megszüntetni azt; 2) ha lemásolódik, akkor lemásolja azt; 3) a nyíl operátoron keresztül pedig pointerszerű interfésszel elérhetővé teszi a tagfüggvényeit.

Miért jó ez? Mert a rajztábla (a heterogén kollekció) most csak ennyi:

class Rajztabla {
    private:
        std::vector<Alakzat> tabla;

    public:
        void felrak(AlakzatOs *ap) {      // kötelezően dinamikusan foglalt
            tabla.push_back( Alakzat(ap) );     // becsomagoljuk
        }

        void rajzol() {
            for (Alakzat & a : tabla)
                a->rajzol();
        }

        void mozgat(Point d) {
            for (Alakzat & a : tabla)
                a->mozgat(d);
        }
};

Ennek nem kell se másoló konstruktort, se destruktort írnunk. A dinamikus tömböt a vektor kezeli, a benne lévő dinamikusan foglalt objektumokat pedig a fenti Alakzat osztály. Sehol egy new, sehol egy delete. Végre a feladatra tudunk koncentrálni!

A végleges változat letölthető innen: heterogen_alakzat_vegleges.zip.