Objekte und Pointer



  • Hultaj schrieb:

    Es geht darum, dass ich eine Variable (int) entscheiden lassen möchte, welche Objekte (also welcher Klasse) angelegt werden... Das Problem ist jetzt aber natürlich, dass die Objekte beim Verlassen der if-else-Kontrollstruktur zerstört werden...

    Ich sehe hier sehr viele Fehler und Wissenslücken. Nicht für ungut, aber ein Blick in ein C++ Grundlagenbuch (und zwar keines das C und C++ gleichzeitig im Namen, oder etwas wie Komplett, von A bis Z usw. enthält) wäre sinnvoll.

    Ich gehe mal auf viele einzelne Punkte ein:
    1. Wenn du zwischen Objekten entscheiden musst, kann es sinnvoll sein sich mit Polymorphie / Vererbung zu beschäftigen (Aber mache nicht den Fehler alles mit Vererbung lösen zu wollen). Ebenso wäre das Thema Objektfabrik / Fabrikmethoden etc. vielleicht noch ein sinnvolles Stichwort.

    2. Objekte sollte man nur mit new allokieren, wenn es wirklich nötig ist. Zudem muss man Objekte die man mit new alloziert auch mit delete freigeben...

    Hultaj schrieb:

    Das Problem ist jetzt aber natürlich, dass die Objekte beim Verlassen der if-else-Kontrollstruktur zerstört werden, ...

    Nicht so wie du vorgehst:

    if (a == 1) {
        Class1 *objectPtr;
        objectPtr = new Class1[n];
    } // <-- Die Objekte existieren noch, nur der Zeiger ist gelöscht.
    

    Hier hast du Objekte mit new erzeugt, aber nicht wieder freigegeben. Diese exisiteren noch, auch wenn du keine Möglichkeit mehr hast darauf zuzugreifen.

    3. Nicht nur SeppJ hat mit "init" so seine Probleme. In der Regel sollte ein Objekt über den Konstruktor initialisiert werden. Nur in seltenen Ausnahmefällen ist eine nachträgliche Initialisierungfunktion (wobei es genau genommen keine Initialisierung mehr sein kann, sondern nur eine Änderung) sinnvoll. Initialisierungsfunktionen deuten häufig auf ein fehlendes Verständnis von der Objektorientierung und Konstruktoren/Destruktoren hin.

    Vielleicht beschreibst du einfach mal mit einfachen Worten was du konkret machen willst, und verzichtest auf Umsetzungsdetails. Dann könnten wir ggf. bessere/saubere Alternativen aufzeigen.



  • Cool, mit dem Definieren vor der If-Abfrage klappt's zumindest schon mal in meinem Minimalbeispiel.

    Aber das heißt ja, dass ich dann wiederum 2 Pointer definieren müsste, oder?

    Also

    Class1 *objectPtr1;
        Class2 *objectPtr2;
    
        if (geom == 1) {
            objectPtr1 = new Class1[n];
            }
        else {
            objectPtr2 = new Class2[n];
    
        }
    
        objectPtr1->init();
    

    Oder ist das umständlich und nicht im Sinne des Erfinders?

    Das Kapitel über Polymorphismus werde ich mir dann mal zu Gemüte führen. Angst vor init()? Warum? 😉



  • Mit Hilfe der Polymorphie kannst Du beide über einen Basisklassenzeiger ansprechen. Das ist aber nur sinnvoll, wenn Class1 und Class2 irgendwie ein gemeinsames Konzept haben, man also irgendeine Generalisierung davon ableiten kann.
    Initialisierungen macht man in C++ im Konstruktor.



  • Ich habe ja auch einen Konstruktor, wobei ich dort eine Initialisierungsliste verwende. Die beiden Klassen haben ein gemeinsames Konzept, sie unterscheiden sich nur durch eine gewisse Geometrie, die ich hier nicht weiter erläutern kann, da es zu kompliziert wäre.

    Ich schaue mir Polymorphie an und werde später noch mal ausführlicher antworten (muss leider los). Mein Buch (C++ von A bis Z) muss ich wohl wechseln... 😉



  • Du könntest mir das Buch per Post senden.



  • Hultaj schrieb:

    Ich habe ja auch einen Konstruktor, wobei ich dort eine Initialisierungsliste verwende. Die beiden Klassen haben ein gemeinsames Konzept, sie unterscheiden sich nur durch eine gewisse Geometrie, die ich hier nicht weiter erläutern kann, da es zu kompliziert wäre.

    Ich schaue mir Polymorphie an und werde später noch mal ausführlicher antworten (muss leider los). Mein Buch (C++ von A bis Z) muss ich wohl wechseln... 😉

    Mit Polymorphie sähe es wohl etwa wie folgt aus (ungetestet):

    #include <iostream>
    
    class IGeometrie // <-- Möglichst passenden Namen
    {
        public:
            // Virtueller Destruktor damit löschen über Basisklassenzeiger
            // sauber funktionert.
            virtual ~IGeometrie() {}
    
            // Gemeinsame Schnittstelle
            virtual void Init() = 0;
    };
    
    class KonkreteKlasseA : public IGeometrie
    {
        public:
            void Init() { std::cout << "A"; }
    };
    
    class KonkreteKlasseB : public IGeometrie
    {
        public:
            void Init() { std::cout << "B"; }
    };
    
    int main()
    {
        int geom = 1;
    
        // Besser mit Smartpointer, ich halte es aber erst einmal nach deinen
        // Beispiel.
        IGeometrie * objektPtr = 0;
    
        // In deinem Beispiel legst du Arrays dieser Klassen an, was aber
        // entweder verkehrt ist, oder zumindest mit dem anschließenden Init-Aufruf
        // zuwider läuft (wenn müsstest du dieses Array durchiterieren, und dies
        // bei allen Elementen des Arrays durchführen).
        if(geom == 1)
            objektPtr = new KonkreteKlasseA;
        else
            objektPtr = new KonkreteKlasseB;
    
        objektPtr->Init();
        delete objektPtr; // An das Freigeben denken! (Smartpointer würden dir
                          // hier unter die Arme greifen).
    }
    


  • Hultaj schrieb:

    Mein Buch (C++ von A bis Z) muss ich wohl wechseln... 😉

    Ja, das solltest du. Falls du vielen Menschen einen Gefallen tun willst, schicke es wirklich an jemanden der sich mal die Muse nimmt es grundlegend auseinander zu nehmen (Ich sage nur Rezensionen wie in Amazon), und dies auch gut begründen kann.

    Nur mir ist das Geld einfach zu viel, nur um Andere vor einem wirklich schlechtes Buch zu warnen.



  • Also zunächst: vielen Dank für die Tipps 🙂

    Zu dem Buch: Ich habe es auch nur in der Uni-Bibliothek ausgeliehen, kann es also niemandem schicken.

    Nun zum eigentlichen Problem: Ich habe mich nun ein wenig mit Polymorphie beschäftigt (anderes Buch). Ich habe vergessen, zu erwähnen, dass "init()" eine virtuelle Funktion in Class1 (Basisklasse) ist, Class2 erbt wiederum von Class1. Dann bräuchte ich die von asc vorgeschlagene Klasse "IGeometrie" theoretisch nicht (oder?).

    Dann würde ich es so aussehen:

    Class1 *ObjectsPtr;
    
    if (geom==1)
       ObjectsPtr = new Class1[n];
    else 
       ObjectsPtr = new Class2[n];    
    
    for (int i=0; i<n; ++i)
    	ObjectsPtr[i]->init(seed); // seed for random numbers
    
    .
    .
    .
    delete ObjectsPtr;
    

    Jetzt muss ich das mal probieren. Wahrscheinlich ist noch irgendwas falsch.



  • Hultaj schrieb:

    Ich habe vergessen, zu erwähnen, dass "init()" eine virtuelle Funktion in Class1 (Basisklasse) ist, Class2 erbt wiederum von Class1. Dann bräuchte ich die von asc vorgeschlagene Klasse "IGeometrie" theoretisch nicht (oder?).

    Stimmt, dann tritt Class1 im wesentlichen in die Rolle von IGeometrie (Wobei ich mal ganz fest hoffe das du sprechendere Namen für deine Klassen wählst).

    Wichtig ist aber:

    a) Der Destruktor von Class1 muss virtuell sein (Da du über den Zeiger der Basisklasse löschst).

    b) Da du mit einem Array arbeitest (unter C++ gibt es mit den Containern der STL eigentlich wesentlich bessere Alternativen, in deinem Fall wären die Zeigercontainer von Boost sogar noch besser) musst du den Speicher mit "delete[] ObjectsPtr" statt "delete ObjectsPtr" löschen.



  • Hultaj schrieb:

    ...
    Zu dem Buch: Ich habe es auch nur in der Uni-Bibliothek ausgeliehen, kann es also niemandem schicken.
    ...

    Kleb´ nen Zettel in den Einband, der vor der Lektüre warnt!



  • asc schrieb:

    b) Da du mit einem Array arbeitest [...] mit "delete[] ObjectsPtr" statt "delete ObjectsPtr" löschen.

    Oh, das mit dem Array ist eine nicht ganz unwichtige Feststellung. Zeigerarithmetik und Polymorphie beißen sich. Das geht so gar nicht, wie er es vorhatte, da sizeof(Class1) sicherlich nicht gleich sizeof(Class2) sein wird.

    Ich würde auch eine flache Hierarchie mit abstrakter Basisklasse vorziehen.

    boost::ptr_vector<ABC> vec; // ABC = abstract base class
    if (geom==1) {
       for (int i=0; i<n; ++i) vec.push_back(new Class1(...));
    } else {
       for (int i=0; i<n; ++i) vec.push_back(new Class2(...));
    }
    
    for (int i=0; i<n; ++i)
       vec[i].init(seed); // seed for random numbers
    
    .
    .
    .
    // delete ObjectsPtr; <-- braucht man hier nicht
    

Anmelden zum Antworten