Kihagyás

Órai feladat

2. Gyakorlat

1. Első osztály

  • Készítsünk egy osztályt Tile néven! Egy Tile egy 1x1 méteres területet jelent. Legyen egy pár adattagja az osztálynak:

    • type: string típusú, a terület típusát adja meg, pl.: "homok", "víz", stb.
    • value: nem negatív egész szám típusú, a terület értékét adja meg.
    tile.cpp
    1
    2
    3
    4
    5
    6
    7
    #include <string>
    
    class Tile {
    public:
        std::string type;
        unsigned    value;
    };
    

2. Osztály használata

  • Példányosítsuk egy Tile osztály példányt (avagy hozzunk létre egy ilyen típusú objektumot) a main függvényünkben.

    tile.cpp
    1
    2
    3
    4
    5
    int main() {
        Tile obj;
    
        return 0;
    }
    
  • Fejlesszük tovább a programunkat! Írassuk ki a létrehozott példány minden adattagját, egymás után egy szóközzel elválasztva.

    tile.cpp
        std::cout << "obj.type: " << obj.type
                  << " obj.value: " << obj.value
    
    Mit ír ki a program?
    $ g++ -Wall -Werror -o program tile.cpp
    $ ./program
    obj.type:  obj.value: 1334548968
    
    • Mi történik ha többször futtatjuk le a programot?
    • A string "üres" lett a szám pedig valami furcsaság.
    • Miért van ez?
  • Adjunk értéket a létrehozott objektum adattagjainak a kiiratás előtt

    tile.cpp
        Tile obj;
    
        obj.type = "homok";
        obj.value = 100;
    

3. Const-ans probléma

  • Adjunk egy const módosítót a Tile osztály type adattagjának.

    tile.cpp
        const std::string type;
    
    Sikerül fordítani-e a programot?
    $ g++ -Wall -Werror -o program tile.cpp
    tile.cpp: In function ‘int main()’:
    tile.cpp:13:16: error: no match for ‘operator=’ (operand types are ‘const std::string’ {aka ‘const std::__cxx11::basic_string<char>’} and ‘const char [6]’)
       13 |     obj.type = "homok";
          |                ^~~~~~~
    
    Mi lehet a probléma?

    A probléma az, hogy a Tile type adattagja most már rendelkezik egy const módosítóval, aminek a hatására ezt az adattagot a példány létrehozásakor be kellene állítani, később nem módosítható.

  • Hogyan lehetne javítani?

    Inicializáljuk az értéket az osztályban!
        const std::string type = "homok";
    

4. Igen... de....

  • Szuper, hogy tudjuk az osztályt példányosítani, de mi van ha, nem mindig "homok" típusú területeket szeretnénk létrehozi? Hozzunk, létre egy egyparaméteres konstruktort (constructor, ctor)!

    • Ennek a konstruktornak a terület típusát adjuk át.
    • A kapott paramétterrel közvetlenül inicializáljuk a osztály megfelelő adattagját! (hint: initializer list)
    • Majd, módosítsuk a példányosítás helyét, hogy egy másmilyen típusú területet hozzunk létre.
    Konstruktor hozzáadása
    class Tile {
    public:
        const std::string type;
        unsigned value;
    
        Tile(const std::string& type) // (1)
            : type(type)              // (2)
        {}
    };
    
    1. A típust konstans (const) referenciaként (&)! Később lesz róla szó.
    2. Az initializer listben a külső név az osztály adattagja, a belső pedig a kapott paraméter van valami egyéb utasítás.
  • Alakítsuk tovább a konstruktor! Legyen kétparaméteres, hogy a terület értékét is megkapja.

    Konstruktor adaptálás
        Tile(const std::string& type, unsigned value)
            : type(type), value(value)
    
    • Ne felejtsük el a példányosítás helyén is adaptálni a kódunkat!
  • A konstruktorban írjuk ki az eltárolt adatokat egy sorban a Tile(<type>, <value>) formában.

    Kiíratás
        {
            std::cout << "Tile(" << this->type << ", "
                                 << this->value << ")" << std::endl;
        }
    
    Mit jelent a this-> ?

    A this egy pointer (memóriacím) az éppen aktuálisan használt objektum példányra. Azaz, hogy éppen melyik példánnyal dolgozunk. Ez csak osztály tagfüggvényekben lehet használni.

5. Adatok.. egyém, tiéd, mindenkié?

  • Objektum orientált programozás esetén sok esetben nem célszerű egy osztály minden adattagját publikussá tenni. Rejtsük el a Tile osztály két adattagját, hogy "kívülről" ne lehessen hozzáférni!

    • Legyenek az adattagok privátok!

      Private adattagok
      class Tile {
      private: // (1)
          const std::string type;
          unsigned value;
      public:
          Tile(const std::string& type, unsigned value)
              : type(type), value(value)
      
      1. Minden adattag és függvény a private: után privát láthatóságú lesz, amíg másképpen nem rendelkezünk. Amennyiben nincs kiírva, private láthatoságú lenne.
    • Milyen problémába ütközünk fordításkor? Mit jelent az üzenet?

      Fordítási hiba
      $ g++ -Wall -Werror -o program tile.cpp
      tile.cpp: In function ‘int main()’:
      tile.cpp:20:38: error: ‘const std::string Tile::type’ is private within this context
         20 |     std::cout << "obj.type: " << obj.type
            |                                      ^~~~
      tile.cpp:6:23: note: declared private here
          6 |     const std::string type;
            |                       ^~~~
      tile.cpp:21:40: error: ‘unsigned int Tile::value’ is private within this context
         21 |               << " obj.value: " << obj.value
            |                                        ^~~~~
      tile.cpp:7:14: note: declared private here
          7 |     unsigned value;
            |              ^~~~~
      
    • Mi lenne rá a megoldás, hogy mégis el lehessen érni az adatokat az osztály példányon kívülről?

      Megoldás ötlet

      Adjunk az osztályhoz "getter" eljárásokat, amikkel a belső adattagokat "kipublikáljuk".

          const std::string& getType() { return type; }
          unsigned getValue() { return value; }
      
    • Használjuk ezeket a hozzáadott "getter"-eket a kiíratásnál a main-ben!

6. Többféle konstruktor

  • Oldjuk meg, hogy lehessen egy Tile példányt csak a típusa alapján példányosítani, egy alapértelmezett 10-es value-val. (Ezt többféleképpen is meg lehet csinálni).

    • Adjunk hozzá egy egyparaméteres konstruktors aminek csak egy típust tartalmazó paramétere van.
    • Az inicializációs listában adjunk mindkét adattagnak értéket.
    • Ne felejtsük el, hogy az eredeti konstruktorunkban volt kiíratás, az ide is kell!

      Második konstruktor
          Tile(const std::string& type)
              : type(type), value(10)
          {
              std::cout << "Tile(" << this->type << ", "
                                   << this->value << ")" << std::endl;
          }
      
    • Használjuk is a konstuktor! A main-ben hozzunk létre egy másik példányt, aminek legyen hegy a típusa.

  • Jó.. jó... de mi van ha kell egy másik konstruktor is, ugyanolyan viselkedéssel? Ismét le kellene másolni a konstruktor "belső" működését? Szerencsére nem!

    • Módósítsuk az egyparaméteres konstruktort, hogy az eredeti kétparaméterest hívja meg. (delegate constructor)

      Delegating constructor
          Tile(const std::string& type)
              : Tile(type, 10)
          {}
      
  • Szuper! Legyen egy default konstruktorunk is (nulla paraméteres konstruktor).

    • A default konstruktor esetén az alapértelmezett típus az "ures" szöveg és 0-ás értékű legyen a létrehozott példány.

      Default ctor
          Tile()
              : Tile("ures", 0)
          {}
      
  • Próbáljuk ki különféle értékekkel a konstruktorokat!

    Konstruktorok használata
    int main() {
        Tile obj("viz", 200);
    
        std::cout << "obj.type: " << obj.getType()
                  << " obj.value: " << obj.getValue()
                  << "\n";
    
        Tile other("hegy");
    
        std::cout << "obj.type: " << other.getType()
                  << " obj.value: " << other.getValue()
                  << "\n";
    
        Tile empty; // (1)
    
        std::cout << "obj.type: " << empty.getType()
                  << " obj.value: " << empty.getValue()
                  << "\n";
    
        return 0;
    }
    
    1. a Tile empty(); forma nem helyes, nem konstruktor hívás lenne.

7. Változó értékek

  • Sok esetben példányosítás után valamilyen adatot módosítani szeretnék, például a területünk értékét. Jelen esetben azonban ez nem lehetséges. Segítsünk ezen!

    • Hozzunk létre "setter"-t a value adattaghoz.

      Value setter
          void setValue(unsigned newValue) {
              value = newValue;
          }
      
    Létrehozhatunk-e a type-hoz settert?

    Mivel a type adattag az osztályban const-ként van megadva, így inicializálás után már nem módosíthatjuk. Tehát nincs értelme a "setter"-nek.

  • Használjuk a "setter"-t minden példányosítás után, de a kiíratások előtt a main-ben.

    Setter használata
        Tile obj("viz", 200);
        obj.setValue(400);
    

8. Destruktor

  • Sok-sok példányt létrehoztunk, de lehet "takarítani" is kellene. C++ példány megszünésekor van lehetőség destruktor meghívására (sok esetben automatikusan).

    • Adjuk a Tile osztályhoz egy destruktort (dtor)!
    • A destruktorban írjuk ki az eltárolt adatokat egy sorban a ~Tile(<type>, <value>) formában.
    ~Tile
        ~Tile() {
            std::cout << "~Tile(" << this->type << ", "
                                  << this->value << ")" << std::endl;
        }
    
    • Mikor fut le a destruktor?

9. Kis "takarítás"

  • Jelenleg több példányt is létrehozunk és mindegyikhez van 1-1 kiíratást végző kódrészlet másolna/begépelve. Azonban nem kell nekünk ennyi kód duplikáció.

    • Készítsünk egy függvényt display néven ami egy Tile példányt kap és kiírja a típusát, értékét hasonló formában mint eddig. (&-t most ne használjunk!)
    display(..)
    void display(Tile instance) {
        std::cout << "..type: " << instance.getType()
                  << "..value: " << instance.getValue()
                  << "\n";
    }
    
  • Használjuk ezt a display függvényt a main-ben.

    display(..)
    int main() {
        Tile obj("viz", 200);
        obj.setValue(400);
    
        display(obj);
    
        Tile other("hegy");
        other.setValue(10000);
    
        display(other);
    
        Tile empty;
        empty.setValue(33);
    
        display(empty);
    
        return 0;
    }
    
  • A program végrehajtása után mit ír ki?

    Fordítás, végrehajtás és kimenet
    $ g++ -Wall -o tile tile.cpp
    $ ./tile
    Tile(viz, 200)
    ..type: viz..value: 400
    ~Tile(viz, 400)
    Tile(hegy, 10)
    ..type: hegy..value: 10000
    ~Tile(hegy, 10000)
    Tile(ures, 0)
    ..type: ures..value: 33
    ~Tile(ures, 33)
    ~Tile(ures, 33)
    ~Tile(hegy, 10000)
    ~Tile(viz, 400)
    
    Miért van ilyen sok ~Tile kimenet?

    Ez a display függvény paraméteréből jön. Jelen esetben a Tile-t Tile instance-ként adjuk át, ami azt jelenti, hogy mindig másolással adjuk át az értéket amikor ezt az eljárást meghívjuk.

10. Paraméter átadás

  • Az előző feladat végén láthattuk, hogy az osztály destruktora többször is lefut mint gondolnánk.

    • Oldjuk meg, hogy a display eljárás hívás hatására ne másolódjon le a kapott Tile típusú adat.
    • Ehhez legyen az eljárás paramétere Tile referencia.
    Tile referencia
    void display(Tile& instance) { // (1)
    
    1. A Tile& fontos! A & jelenti azt, hogy ez egy referencia típus. Egy referencia mindig mutat valami létező értékre, nem történik másolás.
    Fordítás, végrehajtás és kimenet
    $ g++ -Wall -o tile tile.cpp
    $ ./tile
    Tile(viz, 200)
    ..type: viz..value: 400
    Tile(hegy, 10)
    ..type: hegy..value: 10000
    Tile(ures, 0)
    ..type: ures..value: 33
    ~Tile(ures, 33)
    ~Tile(hegy, 10000)
    ~Tile(viz, 400)
    

11. Védelem a módosítások ellen

  • Most, hogy van egy kiírató függvényünk egyből van is vele egy kis probléma: Ez a kiíró függvény módosíthatja a kapott Tile példányt. De egy csak kiíratást végző eljárást miért módosítaná az kapott példányt?

    • Oldjuk meg, hogy ne lehessen a display eljárásban módosítani a kapott példányt.

      Megoldás ötlet

      Adjunk egy const módosítót a display eljárás első paraméteréhez.

      Módosított fejléc
      void display(const Tile& instance) { // (1)
      
      1. A const-ot jelen esetben a referencia típus elé kell megadni.
    • Okés... és fordul a program? Gyakori hibák

      Fordítási hiba
      $ g++ -Wall -o tile tile.cpp
      tile.cpp: In function ‘void display(const Tile&)’:
      tile.cpp:38:48: error: passing ‘const Tile’ as ‘this’ argument discards qualifiers [-fpermissive]
         38 |     std::cout << "..type: " << instance.getType()
            |                                ~~~~~~~~~~~~~~~~^~
      tile.cpp:29:24: note:   in call to ‘const std::string& Tile::getType()’
         29 |     const std::string& getType() { return type; }
            |                        ^~~~~~~
      tile.cpp:39:50: error: passing ‘const Tile’ as ‘this’ argument discards qualifiers [-fpermissive]
         39 |               << "..value: " << instance.getValue()
            |                                 ~~~~~~~~~~~~~~~~~^~
      tile.cpp:30:14: note:   in call to ‘unsigned int Tile::getValue()’
         30 |     unsigned getValue() { return value; }
            |              ^~~~~~~~
      
      Hiba oka

      A hiba röviden az, hogy C++-ban minden eljárást amit const típusú példányon akarunk végrehajtani jelölni kell.

  • Javítsuk meg a programunkat!

    • Adjunk egy-egy const módosítót a "getter" eljárások fejléceinek végére.

      Módosított getter fejlécek
          const std::string& getType() const { return type; }
          unsigned getValue() const { return value; }
      
    • Próbáljuk ki újra a programunkat!


Megoldások
1. feladat
1
2
3
4
5
6
7
#include <string>

class Tile {
public:
    std::string type;
    unsigned    value;
};
2. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>

class Tile {
public:
    std::string type;
    unsigned    value;
};

int main() {
    Tile obj;

    obj.type = "homok";
    obj.value = 100;

    std::cout << "obj.type: " << obj.type
              << " obj.value: " << obj.value
              << "\n";

    return 0;
}
3. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>

class Tile {
public:
    const std::string type = "homok";
    unsigned value;
};

int main() {
    Tile obj;

    obj.value = 100;

    std::cout << "obj.type: " << obj.type
              << " obj.value: " << obj.value
              << "\n";

    return 0;
}
4. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>

class Tile {
public:
    const std::string type;
    unsigned value;

    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }
};

int main() {
    Tile obj("viz", 200);

    std::cout << "obj.type: " << obj.type
              << " obj.value: " << obj.value
              << "\n";

    return 0;
}
5. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    const std::string& getType() { return type; }
    unsigned getValue() { return value; }
};

int main() {
    Tile obj("viz", 200);

    std::cout << "obj.type: " << obj.getType()
              << " obj.value: " << obj.getValue()
              << "\n";

    return 0;
}
6. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    Tile(const std::string& type)
        : Tile(type, 10)
    {}

    Tile()
        : Tile("ures", 0)
    {}

    const std::string& getType() { return type; }
    unsigned getValue() { return value; }
};

int main() {
    Tile obj("viz", 200);

    std::cout << "obj.type: " << obj.getType()
              << " obj.value: " << obj.getValue()
              << "\n";

    Tile other("hegy");

    std::cout << "obj.type: " << other.getType()
              << " obj.value: " << other.getValue()
              << "\n";

    Tile empty; // (1)

    std::cout << "obj.type: " << empty.getType()
              << " obj.value: " << empty.getValue()
              << "\n";

    return 0;
}
7. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    Tile(const std::string& type)
        : Tile(type, 10)
    {}

    Tile()
        : Tile("ures", 0)
    {}

    const std::string& getType() { return type; }
    unsigned getValue() { return value; }

    void setValue(unsigned newValue) {
        value = newValue;
    }
};

int main() {
    Tile obj("viz", 200);
    obj.setValue(400);

    std::cout << "obj.type: " << obj.getType()
              << " obj.value: " << obj.getValue()
              << "\n";

    Tile other("hegy");
    other.setValue(10000);

    std::cout << "obj.type: " << other.getType()
              << " obj.value: " << other.getValue()
              << "\n";

    Tile empty;
    empty.setValue(33);

    std::cout << "obj.type: " << empty.getType()
              << " obj.value: " << empty.getValue()
              << "\n";

    return 0;
}
8. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    Tile(const std::string& type)
        : Tile(type, 10)
    {}

    Tile()
        : Tile("ures", 0)
    {}

    ~Tile() {
        std::cout << "~Tile(" << this->type << ", "
                              << this->value << ")" << std::endl;
    }

    const std::string& getType() { return type; }
    unsigned getValue() { return value; }

    void setValue(unsigned newValue) {
        value = newValue;
    }
};

int main() {
    Tile obj("viz", 200);
    obj.setValue(400);

    std::cout << "obj.type: " << obj.getType()
              << " obj.value: " << obj.getValue()
              << "\n";

    Tile other("hegy");
    other.setValue(10000);

    std::cout << "obj.type: " << other.getType()
              << " obj.value: " << other.getValue()
              << "\n";

    Tile empty;
    empty.setValue(33);

    std::cout << "obj.type: " << empty.getType()
              << " obj.value: " << empty.getValue()
              << "\n";

    return 0;
}
9. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    Tile(const std::string& type)
        : Tile(type, 10)
    {}

    Tile()
        : Tile("ures", 0)
    {}

    ~Tile() {
        std::cout << "~Tile(" << this->type << ", "
                              << this->value << ")" << std::endl;
    }

    const std::string& getType() { return type; }
    unsigned getValue() { return value; }

    void setValue(unsigned newValue) {
        value = newValue;
    }
};

void display(Tile instance) {
    std::cout << "..type: " << instance.getType()
              << "..value: " << instance.getValue()
              << "\n";
}


int main() {
    Tile obj("viz", 200);
    obj.setValue(400);

    display(obj);

    Tile other("hegy");
    other.setValue(10000);

    display(other);

    Tile empty;
    empty.setValue(33);

    display(empty);

    return 0;
}
10. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    Tile(const std::string& type)
        : Tile(type, 10)
    {}

    Tile()
        : Tile("ures", 0)
    {}

    ~Tile() {
        std::cout << "~Tile(" << this->type << ", "
                              << this->value << ")" << std::endl;
    }

    const std::string& getType() { return type; }
    unsigned getValue() { return value; }

    void setValue(unsigned newValue) {
        value = newValue;
    }
};

void display(Tile& instance) { // (1)
    std::cout << "..type: " << instance.getType()
              << "..value: " << instance.getValue()
              << "\n";
}


int main() {
    Tile obj("viz", 200);
    obj.setValue(400);

    display(obj);

    Tile other("hegy");
    other.setValue(10000);

    display(other);

    Tile empty;
    empty.setValue(33);

    display(empty);

    return 0;
}
11. feladat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <string>

class Tile {
private:
    const std::string type;
    unsigned value;
public:
    Tile(const std::string& type, unsigned value)
        : type(type), value(value)
    {
        std::cout << "Tile(" << this->type << ", "
                             << this->value << ")" << std::endl;
    }

    Tile(const std::string& type)
        : Tile(type, 10)
    {}

    Tile()
        : Tile("ures", 0)
    {}

    ~Tile() {
        std::cout << "~Tile(" << this->type << ", "
                              << this->value << ")" << std::endl;
    }

    const std::string& getType() const { return type; }
    unsigned getValue() const { return value; }

    void setValue(unsigned newValue) {
        value = newValue;
    }
};

void display(const Tile& instance) { // (1)
    std::cout << "..type: " << instance.getType()
              << "..value: " << instance.getValue()
              << "\n";
}


int main() {
    Tile obj("viz", 200);
    obj.setValue(400);

    display(obj);

    Tile other("hegy");
    other.setValue(10000);

    display(other);

    Tile empty;
    empty.setValue(33);

    display(empty);

    return 0;
}

Utolsó frissítés: 2026-09-30
Létrehozva: 2026-09-30