Von std::string erben



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



  • @_ro_ro Generell ist mein Eindruck, das man versucht man in C++ Laufzeitpolymorphie zu vermeiden.
    Tatsächlich benötigt man es selten, denn meistens ist vorher bekannt, welche Funktion tatsächlich ausgeführt werden soll. Und dafür möchte man den Performanceverlust durch die Indirektion über den v-table vermeiden.

    In C++ gibt es verschiedene Möglichkeiten für "Compile Time Polymorphism", umgesetzt z.B. über Templates. Da wird direkt zu Compiletime entschieden, welche Funktion aufgerufen werden soll. Ein Stichwort zur Suche ist z.B. CRTP.

    Wenn ich mir überlege, dass ich zum Beispiel einen Split auf einem string brauche, hat deine Implementierung z.B: schon das Problem, dass sie nur auf std::string also std::base_string<char> funktioniert und nicht auf std::wstring und anderen Varianten (https://en.cppreference.com/w/cpp/string/basic_string).

    Vielleicht wäre es sogar möglich einen Algorithmus zu schreiben, der so eine Art Split auf ganz unterschiedlichen Containern durchführen kann? Warum z.B. soll man einen Vektor nicht an einer bestimmten Stelle splitten können?

    Mit C++20 (auch schon 4 Jahre alt) haben wir zum Beispiel std::ranges::views::split (https://en.cppreference.com/w/cpp/ranges/split_view) was das lösen soll und mit C++ 23 ist es sogar für Strings sinnvoll nutzbar 😉

    #include <ranges>
    #include <iostream>
    #include <algorithm>
    #include <string_view>
    
    int main()
    {
     std::string a = "hallo welt";
    
      for (const auto word : std::views::split(a, std::string(" ")))
            std::cout << std::string_view(word) << "\n";
    
      return 0;
    }
    

    geklaut und angepasst von hier (https://en.cppreference.com/w/cpp/ranges/split_view)

    Zum spielen mit dem Code, siehe: https://godbolt.org/z/n5MsY5e7W



  • @Schlangenmensch

    ungemein wichtiger ist es, daß alle diese Stringfunktionen zwischen bytesemantics und charactersemantics unterscheiden müssen: Wegen UTF-8. Also daß bspw. ein length("ä") bytesemantisch 2 ergibt, zeichenorientiert hingegen 1 sofern dieses 'ä' utf-8-kodiert ist. Denn sonst führt ein substr() u.U. dazu, daß die Einzelbytes von Zeichen ungewollt voneinander getrennt werden.

    Wie geht denn C++ mit diesem Sachverhalt um?

    MFG



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

    Wie geht denn C++ mit diesem Sachverhalt um?

    Gar nicht.

    Musst du selber machen oder darauf spezialisierte Bibliotheken verwenden. Zum Beispiel https://github.com/copperspice/cs_string

    (Es gibt wstring, aber das hat ja eigentlich dasselbe Problem)
    (Du kannst mit stream.imbue arbeiten, aber das erschien mit immer unendlich kompliziert, schau dir dazu https://en.cppreference.com/w/cpp/locale/codecvt an - teilweise eingeführt mit C++11 und mit C++20 wieder deprecated... also besser lieber gar nicht aufmachen, dieses Fass)



  • @wob

    danke, hab'ch mir schon gedacht 😉

    Wie auch immer, wenn man Bytesequenzen nur durchreicht spielt die Zeichenkodierung überhaupt gar keine Rolle. Also strict bytesemantisch arbeiten, so handhabe ich das seit 20 Jahren Web-Anwendung-Programmierung -- Entgegen aller Empfehlungen (die beim genauen Hingucken blödsinnig sind).

    MFG



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

    Mit C++20 (auch schon 4 Jahre alt)

    Das heißt in 10- bis 15 Jahren kommt es in der Industrie an 🙂 Hab doch tatsächlich letztes Jahr eine Firma erlebt, die jetzt so langsam auf c++11 umsteigen will.


Anmelden zum Antworten