protected Vererbung



  • 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.



  • OK es gibt natürlich noch einen Grund für new und delete:
    Die Geschwindigkeit.
    Einen Small-Object-Allocator setzt man ja gerne ein, um das Allokieren von sehr kleinen Objekten drastisch zu beschleunigen (vielleicht 20-mal so schnell). Allerdings auf Kosten der größeren Objekte (vielleicht 0.5% mehr).
    Nun hat man viele viele Möglichkeiten, wo man ein wenig drehen und einstellen kann und letztendlich muß man abschätzen, wie oft der User kleine und wie oft er große Objekte anlegt. Und wie groß? Und wie oft? Und überhaupt?
    In C++ ist es ganz normal, daß man wegen der Laufzeitpolymorphie mit virtuellen Funktionen gleich Millionen von kleinen Objekten mit new anlegt und in einem Vector<Basisklasse*> oder sowas verwaltet. Das ist in C nicht so üblich.
    Naja, und das führt dazu, daß ein schlauer Bauer es so einrichtet, daß malloc eher für größere Objekte gut ist, während new auf jeden Fall bei den kleinen Objekten flott sein muß. Weil es Unterschiede in der typischen Verwendung gibt.
    Also in C++ new nehmen und in C malloc, damit man auch dem Bauer entgegenkommt.
    Deswegen darf man auch nicht was mit operator delete freigeben, was man mit malloc angelegt hat. Es könnten gant andere Implementierungen sein.
    Und deswegen nehme ich kein malloc, wenn ich an anderer Stelle eh operator new nehme, am Ende wird nur mehr Code erzeugt und mehr Speicher durch den Cache gejagt.


Anmelden zum Antworten