Objektumok memóriakezelése belülről

A mai óra célja annak megismerése, hogyan működik az objektumok memóriakezelése belülről.

Felkészülés a gyakorlatra

1. Mitírki

A main melyik sorában a Matrix osztály milyen speciális "tagfüggvényei" hívódnak, milyen sorrendben?

class Matrix
{
    // ...
};

Matrix f1()
{
    Matrix m(3, 3);
    return m;
}

void f2(Matrix s)
{
    std::cout << s << std::endl;
}

int main()
{
    Matrix m1(1, 3);
    Matrix m2(4, 4);

    Matrix m3 = m2;
    m1 = m3;
    m1 = m2 + m3;
    m2 += m3;
    Matrix m4 = f1();
    std::cout << m1 + f1() << std::endl;
    f2(m2);
}

Kezel dinamikus memóriát az osztály? Ebből mennyit lát, aki az osztályt használja? Melyik OOP alapelv hogyan járul hozzá ehhez?

2. Indexelés

Hogyan érjük el a Mátrix egyes elemeit?

Ha az igazi indexelő operátort akarnánk használni, sok problémába futhatunk bele:

Matrix m(2, 4);
std::cout << m[1, 3];   // BAJ VAN, kétparaméteres indexelő operátor nincs...
std::cout << m[3];      // ...és a vessző operátor miatt ez történne a fenti sorban is

std::cout << m[1][3];   // így oké lenne, de...
std::cout << m[1];      // ...ez milyen típus? egész sor? vagy oszlop?
std::cout << m[][3];    // ...és ha van sor, akkor miért nincs oszlop?

Trükk: a mátrixok nekünk amúgy is csak egyes elemek szintjén érdekes, teljes sorokat / oszlopokat ne akarjunk kérni. Az elemek elérésére használjunk kétparaméteres függvényhívás operátort! Melyik zárójelpár mit jelent?

Matrix m(2, 4);
std::cout << m(1, 3) << std::endl;
std::cout << Matrix(2, 4)(1, 3) << std::endl;

return Matrix(2, 4);
Megoldás
  • konstruktor
  • indexelő operátor
  • konstruktor, aztán indexelő operátor
  • konstruktor

3. Adatszerkezet

Mivel az elemeket csak egyesével érheti el kívülről, aki az osztályt használja, ezért nem muszáj oszloponként / soronként tárolni, lehet sorfolytonosan is, InfoC-n a 0. módszer is használható.

sorfolytonos

Ezzel sokkal egyszerűbbé válik a memóriakezelés. Milyen adattagokra van szükség? Hogyan fog működni az indexelés? Írjátok meg a függvényhívás operátorokat! Miért kell belőle kettő?

Ha csak egyesével lehet hozzáférni az elemekhez, akkor a belső implementációnak jóval több szabadságot adunk: lehetne pl. ritka mátrixként tárolni, csak a nemnulla elemeket téve egy láncolt listába, memóriahatékonyan.

4. Memóriakezelés

Írjátok meg a konstruktorokat, destruktort, operator=-t! Miért van szükség mindháromra?

5. Összeadás

A + és a += operátor lehet globális, vagy érdemes tagfüggvényként implementálni? Írjuk meg a Matrix összeadó operátorait!

Tipp

Ha a magasságot és a szélességet le lehet kérdezni a mátrixtól, akkor az operátorok könnyen lehetnek globálisak.

Ha viszont tagfüggvényként implementáljuk, nem kell két ciklus a bejáráshoz, elég egy, hiszen ismerjük az adatszerkezetet, lehet sorfolytonos a bejárás.

Mi írjuk meg globálisként!

6. Transzponált

Írjuk meg azt a műveletet, ami transzponál egy mátrixot! Milyen operátort használjunk erre?

Tipp

Nincs olyan C++ operátor, ami egyértelműen transzponáltat jelent. Ezért legyen inkább névvel rendelkező tagfüggvény!

Megoldás