protected Vererbung



  • CSpille schrieb:

    Zeus schrieb:

    Double hat die Große 8
    Int hat die Größe 4
    Pointer ist ebenfalls 4.

    Diese Größen sind (strenggenommen) Compiler- und System-abhängig.
    Bei meinem 64-bit System ist ein Pointer z. B. 8 Byte.
    -> Ich vermute du hast (noch) ein 32-bit System?

    Ups ich hab ja auch ein 64 Bit Windows 7 Betriebssystem.



  • aber :

    int main()
    {
    int y=7;
    int *x=&y;	
    
    cout<<sizeof(x);  // Ausgabe 4 Byte !!!
    }
    

  • Mod

    Dann hast du dein Programm wohl für 32 Bit kompiliert.



  • blurry333 schrieb:

    WAnn macht es Sinn den Destruktor selbst zu definieren. ABgesehen von virtual destruktor ?

    Einen eigenen Destruktor brauchst Du immer dann, wenn der Defaultdestruktor nicht alles abräumen kann. Der Defaultdestruktor ruft einfach die Destruktoren aller Membervariablen auf. Sonst macht der nichts. Wenn eine Deiner Membervariablen z.B. ein Pointer ist, mußt Du einen eigenen Destruktor erzeugen, der den Speicher eventuell frei gibt:

    class x
    {
        TYPE *pointer;
        x() { pointer=NULL }
        ~x() { if( pointer ) free(pointer); }    // oder delete pointer;
    
        ....
    };
    

    Der Default destruktor würde den Speicher nicht freigeben. Gleiches gilt auch für Filehandles, Fensterhandles und ähnliches.

    mfg Martin



  • mgaeckler schrieb:

    ~x() { if( pointer ) free(pointer); }    // oder delete pointer;
    

    mgaeckler meinte natürlich:

    ~x() { if( pointer ) delete pointer; }    // oder (in Ausnahmen) free(pointer);
    

    Jetzt verführ keinen Neuling dazu malloc und free zu benutzen 😮

    @blurry333: In C++ verwendet man i. d. R. new und delete 😉



  • blurry333 schrieb:

    oki 🙂

    Was ich immer noch nicht versteh welchen Sinn macht es,
    einen Basisklassen Zeiger auf eine abgeleitete Klasse zeigen zu lassen.

    Ich habe beispielsweise eine Klassenbibliothek für die Windows API.

    Die Basisklasse kapselt das Fensterhandle. Abgeleitete Klassen spezialisieren sich dann immer mehr:

    Selbstdefinierte Windowsklassen,
    Edit Controls, Comboboxen, Listboxen, MDI-Fenster, MDI-Childs usw. usf.

    Einer meiner Container enthält eine Liste aller Fenster mit selbst definierten Klassen. Der Container selber kennt aber nur die BASIS-Klasse aller selbst defnierten Fenster. Applikationsspezifische Klassen kann er gar nicht kennen, da er Teil der Bibliothek ist.

    Er liefert daher immer nur einen Zeiger auf die Basisklasse zurück. Wenn der Anwendung in dem Kontext das auch genügt, wird sie das auch so lassen. Ansonsten muß halt gecastet werden.

    Der Container ist auch dafür verantworlich, Windows Messages an die Instanzen der Fensterklassen zu verteilen. Dafür sind dann die virtuellen Funktionen zwingend erforderlich, da er applikationsspezifische Messagehandler gar nicht kennen kann.

    Generel brauchst Du das, wenn ein Container Zeiger auf Instanzen von Klassen speichern muß, bei denen er nur die Basisklasse kennt.

    mfg Martin



  • CSpille schrieb:

    Jetzt verführ keinen Neuling dazu malloc und free zu benutzen 😮

    Was soll an

    char *x=new char[100];
    ...
    delete [] x;
    

    besser sein als an

    char *x =(char *)malloc( 100 );
    ...
    free( x );
    

    ?

    OK, ich muß den Zeiger nicht casten, aber das ist ja wohl ein akademischer Unterschied.

    Hab ich was übersehen?

    mfg Martin



  • mgaeckler schrieb:

    CSpille schrieb:

    Jetzt verführ keinen Neuling dazu malloc und free zu benutzen 😮

    Was soll an

    char *x=new char[100];
    ...
    delete [] x;
    

    besser sein als an

    char *x =(char *)malloc( 100 );
    ...
    free( x );
    

    ?

    OK, ich muß den Zeiger nicht casten, aber das ist ja wohl ein akademischer Unterschied.

    Hab ich was übersehen?

    mfg Martin

    Weiss nicht ob Du's übersehen hast, aber...
    Bei new und delete wird der Konstruktor und Destruktor aufgerufen. Bei malloc und free nicht. Spielt bei char keine Rolle... aber eben nicht geeignet, das dem Anfänger hinzuschmeissen.

    Simon



  • Nicht gut:

    mgaeckler schrieb:

    class x
    {
        TYPE *pointer;
        x() { pointer=NULL }
        ~x() { if( pointer ) free(pointer); }    // oder delete pointer;
    
        ....
    };
    

    Besser:

    class x 
    {
    public:
       x() : pointer(new x())   // Initalisierungsliste 
       {
       }
    
       ....
    
    private:
       x* pointer;
    }
    


  • mgaeckler schrieb:

    OK, ich muß den Zeiger nicht casten, aber das ist ja wohl ein akademischer Unterschied.

    Als C++-Programmierer bevorzugst du ja wohl (i. d. R.) ein std::vector<char> ,oder? 😉



  • theta schrieb:

    Weiss nicht ob Du's übersehen hast, aber...
    Bei new und delete wird der Konstruktor und Destruktor aufgerufen. Bei malloc und free nicht. Spielt bei char keine Rolle... aber eben nicht geeignet, das dem Anfänger hinzuschmeissen.

    Simon

    Richtig bei Klasseninstanzen sind natürlich malloc und free *VERBOTEN*.

    Aber bei allen anderen benutze ich (vieleicht auch einfach aus Gewohnheit) nach wie vor malloc und free.

    mfg Martin



  • Zeus schrieb:

    Besser:

    class x 
    {
    public:
       x() : pointer(new x())   // Initalisierungsliste 
       {
       }
    
       ....
       
    private:
       x* pointer;
    }
    

    ui... Nice endless loop... Aber egal... 🙄

    Was für einen Vorteil hat denn die Initialisierungsliste, wenn ich einen
    Zeiger zuweise?

    Klar, bei Referenzen ist es notwendig.

    Ist das nur stilistisch oder auch ein anderer Vorteil?



  • mgaeckler schrieb:

    Richtig bei Klasseninstanzen sind natürlich malloc und free *VERBOTEN*.

    Aber bei allen anderen benutze ich (vieleicht auch einfach aus Gewohnheit) nach wie vor malloc und free.

    OK es gibt natürlich noch einen Grund für new und delete (bei Anfängern):

    TYPE *ptr = (TYPE*)malloc( 100 * sizeof( TYPE ) );

    vs.

    TYPE *ptr = new TYPE[100];

    Mal schauen, ob ich in meinen Beispielen in Zukunft new statt malloc nehmen kann. 🙄

    mfg Martin



  • Was steht sonst in den Zeiger drin?

    Ganz einfach schrott.

    Für das delete breitet glaub keine Probleme wenn du eine nicht gültige Adresse deletes, aber deine Programmlogik geht wohl flöten. Außerdem bei komplexeren Klassen wenn du mehrere Member und Basisklassen hast, wird zu komplex den Pointer nicht zu initialisieren, dann aber eher mit 0 oder ne Übergabe^^.



  • Zeus schrieb:

    Nicht gut:

    mgaeckler schrieb:

    class x
    {
        TYPE *pointer;
        x() { pointer=NULL }
        ~x() { if( pointer ) free(pointer); }    // oder delete pointer;
    
        ....
    };
    

    Besser:

    class x 
    {
    public:
       x() : pointer(new x())   // Initalisierungsliste 
       {
       }
    
       ....
       
    private:
       x* pointer;
    }
    

    Das würd ich nicht machen. Wenn der Konstruktor von x einen neues Objekt von x erzeugt, wird der Konstruktor von x nochmal aufgerufen, der wieder ... naja irgendwann einmal gibt es einen Stack overflow oder Deine Maschine trasht.



  • mgaeckler schrieb:

    Aber bei allen anderen benutze ich (vieleicht auch einfach aus Gewohnheit) nach wie vor malloc und free.

    Warum? wirkt für mich sehr inkonsistent. Vergleiche darüber hinaus auch folgende Anweisungen bezüglich Länge und Lesbarkeit:

    int *i = (int*)malloc(sizeof(int));
    int *i = new int;
    

    Ein Problem ist auch noch, dass dir die Regel bei generischer Programmierung um die Ohren fliegt. Wenn du z.B. ein typedef int IntType hast und du das später durch eine Integer-Klasse ersetzen möchtest, fliegt dir das um die Ohren, weil du malloc und free verwendet hast. Bei new und delete wären keine Anpassungen notwendig gewesen. Zusammengefasst gibt es also viele Nachteile und keine Vorteile von malloc und free vs. new und delete.

    CSpille schrieb:

    Ist das nur stilistisch oder auch ein anderer Vorteil?

    Wenn man die Initianlisierungsliste nicht nutzt, sondern die Werte im Konstruktor zuweist, wird für die Member vorher der Default-Konstruktor aufgerufen. Mit der Initialisierungsliste ruft man explizit einen evntl. anderen Konstruktor auf. Das spart Performance, weil der Default-Konstruktor entfällt und ist nötig bei Typen, die gar keinen Default-Konstruktor haben (wie eben z.B. Referenzen). Bei Zeigern wird es keine Rolle spielen, weil der Compiler das sicher wegoptimiert, aber auch hier gibt es keinen Grund, bewusst inkonsistent zu sein und Zeiger anders als andere Typen zu behandeln.



  • Zeus schrieb:

    Was steht sonst in den Zeiger drin?

    Ganz einfach schrott.

    Für das delete breitet glaub keine Probleme wenn du eine nicht gültige Adresse deletes, aber deine Programmlogik geht wohl flöten. Außerdem bei komplexeren Klassen wenn du mehrere Member und Basisklassen hast, wird zu komplex den Pointer nicht zu initialisieren, dann aber eher mit 0 oder ne Übergabe^^.

    Klar...

    Ich meinte ja auch gegenüber einer Zuweisung im Body des Konstruktors...
    Okay, bis zu Zuweisung darf ich nicht drauf zugreifen.

    Anders ist das natürlich problematisch...



  • mgaeckler schrieb:

    Das würd ich nicht machen. Wenn der Konstruktor von x einen neues Objekt von x erzeugt, wird der Konstruktor von x nochmal aufgerufen, der wieder ... naja irgendwann einmal gibt es einen Stack overflow oder Deine Maschine trasht.

    Mir geht's um das Sprachelement und nicht um die Logik. Und du weisst warum ne besser Initialisierungsliste ist, oder?



  • CSpille schrieb:

    Zeus schrieb:

    Was steht sonst in den Zeiger drin?

    Ganz einfach schrott.

    Für das delete breitet glaub keine Probleme wenn du eine nicht gültige Adresse deletes, aber deine Programmlogik geht wohl flöten. Außerdem bei komplexeren Klassen wenn du mehrere Member und Basisklassen hast, wird zu komplex den Pointer nicht zu initialisieren, dann aber eher mit 0 oder ne Übergabe^^.

    Klar...

    Ich meinte ja auch gegenüber einer Zuweisung im Body des Konstruktors...
    Okay, bis zu Zuweisung darf ich nicht drauf zugreifen.

    Anders ist das natürlich problematisch...

    Beim Pointer ist naja nix schlimmes. Interessant wird es beim komplexe Objekte. Nämlich dann wenn die Initialisierung der Objekte richtig teuer ist. Die Initialisierungsliste ist das einziges Sprachmittel, wo du es effizient steuerst. Weil bis zum Konstruktorbody wurden schon die Standardkonstruktor der Basisklassen und Members aufgerufen. Hast du die Initialisierungsliste nicht angegeben und im Body nochmal initialisiert, zahlst du doppelt.



  • ipsec schrieb:

    CSpille schrieb:

    Ist das nur stilistisch oder auch ein anderer Vorteil?

    Wenn man die Initianlisierungsliste nicht nutzt, sondern die Werte im Konstruktor zuweist, wird für die Member vorher der Default-Konstruktor aufgerufen. Mit der Initialisierungsliste ruft man explizit einen evntl. anderen Konstruktor auf. Das spart Performance, weil der Default-Konstruktor entfällt und ist nötig bei Typen, die gar keinen Default-Konstruktor haben (wie eben z.B. Referenzen). Bei Zeigern wird es keine Rolle spielen, weil der Compiler das sicher wegoptimiert, aber auch hier gibt es keinen Grund, bewusst inkonsistent zu sein und Zeiger anders als andere Typen zu behandeln.

    Danke ipsec!

    Meine Frage bezog sich nur auf Zeiger (und einfache Datentypen), denn der Aufruf des Default-Konstruktors
    war mir bekannt.

    Aber bei Zeigern brauch der Compiler nichts wegoptimieren 😉
    Es gibt ja keine Default-Zuweisung mit z.B. 0

    Inkonsistenz setze ich mal gleich mit stilistisch 🤡
    Ich bemühe mich die Zuweisung immer in der Initialisierungsliste zu machen.

    Ich wollte nur wissen, ob ein Unterschied zwischen beiden Methoden ist,
    falls es theoretisch mal nicht möglich ist.


Anmelden zum Antworten