Von std::string erben



  • Mahlzeit;
    Idee ist, zum Bau einer split Methode von std::string zu erben. Bisher habe ich

    class mystring : public string{
      private: 
        string origin;
      public:
        mystring(const string &str) : string(str){
            origin = str;
        };
        void split(const char delimiter){
            for( size_t cpos = 0; cpos < origin.length(); cpos++){
                size_t fpos = origin.find(delimiter, cpos);
                string chunk = origin.substr(cpos, fpos - cpos);
                cpos += chunk.length() + 1;
                cout << chunk << endl;
            }
        };
    };
    

    und meine Frage ist, was es da noch so zu beachten gibt. Der Return-Value ist noch nicht näher festgelegt.

    MFG



  • @_ro_ro Warum willst du von std::string erben? Warum nicht einfach eine freie Funktion, die das für dich macht? Oder, wenn es eine Klasse sein soll, warum nicht eine Klasse die einfach einen std::string als Member hat.
    Was mich auch zu der Frage bringt: Warum erbst du von std::string und hast einen std::string Member?

    P.S. std::string hat keinen virtuellen Destruktor, das birgt also die Gefahr, dass mystring nicht richtig freigegeben wird, wenn du nur irgendwo einen Pointer auf die Base Klasse speicherst und darüber deinen mystring freigeben möchtest. (Ich meine, das wäre dann undefined behaviour).



  • @Schlangenmensch sagte in Von std::string erben:

    @_ro_ro Warum willst du von std::string erben?

    Weil ich was lernen will.

    Was mich auch zu der Frage bringt: Warum erbst du von std::string und hast einen std::string Member?

    Genau diese Frage hat mich auch beschäftigt. Und so habe ich darüber nachgedacht und bin zu dem Schluss gekommen, daß ich diesen Member gar nicht brauche, denn das Original steckt ja in std::string origin = *this;.

    Danke für Deine Hinweise!!!

    MFG

    PS: Der Destruktor meiner mystring Class wird aufgerufen, das habe ich geprüft.



  • @_ro_ro Wenn du was lernen möchtest, solltest du lernen, nicht von Klassen zu erben, die keinen virtuellen Destruktor haben.

    std::string* base = new mystring("It's a string");
    delete base;
    

    Mit einem virtuellen Destruktor würde der Destruktor von mystring aufgerufen, ohne hast du keine Chance über den Pointer auf die Basisklasse dein Objekt aufzuräumen.



  • @Schlangenmensch

    bisher habe ich gelernt, new nicht zu verwenden. Und was ist jetzt ein virtueller Destruktor?

    MFG



    1. Man erbt nicht von den std::-Klassen. Siehe u.a. https://quuxplusone.github.io/blog/2018/12/11/dont-inherit-from-std-types/

    2. Manchmal muss man eben Objekte im Freispeicher erzeugen. Ob das mit new oder std::make_unique geschieht, ist zwar für das Freigeben relevant, aber nicht für diese Fragestellung. Das Problem existiert also auch, ohne dass du explizit new rufst.

    3. Siehe https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c127-a-class-with-a-virtual-function-should-have-a-virtual-or-protected-destructor



  • @wob

    Ok wenn das so ist 😉

    Danke wob (Wolfgang?)



  • @_ro_ro Ein virtueller Destruktor ist ein Desturkor, mit dem Keyword virtual. Virtuelle Funktionen generell überschreiben die Funktion des Basisklasse, auch wenn die über einen Pointer auf die Basisklasse aufgerufen werden.

    using namespace std;
    class base
    {
    public:
      virtual ~base() = default;
      virtual void output() { cout << " base\n"; }
    };
    
    class inhereited : public base
    {
    public:
      void output() override { cout << "inherited\n"; }
    };
    
    
    
    int main(int, char const**)
    {
      base* base = new inhereited();
      base->output();
      delete base;
    
      return 0;
    }
    

    Im Falle eines Destruktors gilt im Prinzip dasselbe, nur das sicher gestellt wird, das auch die Base aufgeräumt wird.

    Das new verwende ich nur um schnell ein Beispiel zu haben, in realem Code wäre das ein Smartpointer, was aber am Problem nichts ändert.



  • @_ro_ro sagte in Von std::string erben:

    Danke wob (Wolfgang?)

    Wolfgang. Nein.



  • @Schlangenmensch

    Wieder was gelernt heute 😉

    MFG


  • Gesperrt

    @_ro_ro Rentner-Cop? 😃

    Bei Vererbung macht man sich unter anderem die Eigenschaft zu nutze, dass man den internen Zustand des "Elternobjekts" in der abgeleiteten Klasse ändern darf...



  • @nameName sagte in Von std::string erben:

    @_ro_ro Rentner-Cop? 😃

    Bei Vererbung macht man sich unter anderem die Eigenschaft zu nutze, dass man den internen Zustand des "Elternobjekts" in der abgeleiteten Klasse ändern darf...

    Bei Vererbung wird kein Elternobjekt erstellt sondern eine Instanz der erbenden Klasse.

    MFG



  • @_ro_ro sagte in Von std::string erben:

    @nameName sagte in Von std::string erben:

    @_ro_ro Rentner-Cop? 😃

    Bei Vererbung macht man sich unter anderem die Eigenschaft zu nutze, dass man den internen Zustand des "Elternobjekts" in der abgeleiteten Klasse ändern darf...

    Bei Vererbung wird kein Elternobjekt erstellt sondern eine Instanz der erbenden Klasse.

    MFG

    Ähm... nein. Beim Erstellen des konkreten Objekts werden von alle geerbten Klassen Instanzen erstellt, sie sind halt nur Teil des konkreten Objekts.



  • @DocShoe sagte in Von std::string erben:

    @_ro_ro sagte in Von std::string erben:

    @nameName sagte in Von std::string erben:

    @_ro_ro Rentner-Cop? 😃

    Bei Vererbung macht man sich unter anderem die Eigenschaft zu nutze, dass man den internen Zustand des "Elternobjekts" in der abgeleiteten Klasse ändern darf...

    Bei Vererbung wird kein Elternobjekt erstellt sondern eine Instanz der erbenden Klasse.

    MFG

    Ähm... nein. Beim Erstellen des konkreten Objekts werden von alle geerbten Klassen Instanzen erstellt, sie sind halt nur Teil des konkreten Objekts.

    Nur Wenn der Elternkonstruktor aufgerufen wurde oder ist das grundsätzlich so?

    MFG



  • @_ro_ro
    Grundsätzlich und immer. Der Konstruktor wird immer benötigt, um alle Member einer Klasse zu initialisieren (selbst wenn sie default-constructed sind). Wenn du den Konstruktor einer Basisklasse nicht explizit selbst aufrufst ( durch die : Notation) erzeugt der Compiler einen impliziten Konstruktoraufruf des jeweiligen Defaultkonstruktors (ohne Parameter).

    Edit:
    Ab C++11 kann man die Member direkt in der Deklaration der Klasse initialisieren, das musste man früher im Konstruktor machen. Wenn dann die Konstruktor ender jeweiligen Basisklassen nicht aufgerufen würde, wären die Member höchstens Default-konstruiert, aber nicht mit den Werten, mit denen sie im Konstruktor initialisiert werden.



  • @DocShoe

    danke Dir!! Eigentlich logisch, denn der Destruktor der Elternklasse(n) wird ja immer aufgerufen.

    MFG



  • @_ro_ro

    Nicht zwingend. Mann kann auch Käse programmieren, nehmen wir mal dein Anliegen und benutzen das absichtlich falsch:

    #include <string>
    #include <memory>
    
    class MyString : public std::string
    {
       // hier liegt iwas auf dem Heap, das explizit aufgeräumt werden muss.
    public:
       MyString() = default;
    };
    
    int main()
    {
        // unique_ptr hält einen Zeiger auf die Basisklasse!
       std::unique_ptr<std::string> = std::make_unique<MyString>();
    }
    

    Führt zu einem Problem, weil der Destruktor von MyString nicht aufgerufen wird. Das liegt daran, dass der Destruktor von std::string nicht virtual ist und daher den Destruktor der Elternklasse nicht aufruft.
    Deswegen sollte man auch nicht von Klassen erben, die keinen virtuellen Destruktor haben.

    Und wo wir bei dem Ursprungsproblem sind:
    Wirf mal einen Blick auf boost Tokenizer. Die boost-Bibliotheken sind frei und für fast jeden C++ Compiler verfügbar. Die Installation kann etwas umständlich sein, wenn man sie selbst bauen muss, weil es keine Prebuilt Distro für deinen Compiler gibt.



  • ...oder auf sowas wie https://abseil.io/docs/cpp/guides/strings#abslstrsplit-for-splitting-strings

    Boost ist zwar schön, ich finde es aber oft recht kompliziert zu benutzen (nicht, dass abseil nicht auch seine eigenen Probleme hätte...)


  • Gesperrt

    Wie lange darf der Troll @_ro_ro hier noch posten?



  • @wob

    Vererbung ist nicht immer sinnvoll. Erfahrungsgemäß ist Vererbung genau dann zweckmäßig, wenn Objekte um bestimmte Methoden erweitert werden oder gegebene Methoden spezialisiert werden sollen wobei die geerbten Methoden natürlich auch verwendet werden.

    Insofern ist die Idee, das std::string Objekt um eine Methode split zu erweitern alles Andere als abwegig. Zumal 1. das Splitten ohnehin nur auf Strings angewendet wird und 2. sehr oft benötigt wird. Genau das ist der Sinn einer objektorientierten Programmierung.

    Viele Grüße!

    PS: Meine Splitfunktion (classless) funktioniert einwandfrei. Auch mit UTF-8-kodierten Zeichenketten.


Anmelden zum Antworten