Pointer/Referenzen kleine Frage



  • Skym0sh0 schrieb:

    2.- deine Klasseninternas können von aussen geändert werden -> das widerspricht dem Paradigma der Kapselung

    Das heisst, jede Klasse, die Zeiger oder Referenzen auf andere Objekte hält, widerspricht dem Paradigma der Kapselung?

    Nein. Der String gehört nicht zum Zustand des Objekts, sondern nur der Verweis darauf.

    cooky451 schrieb:

    @Skym0sh0 Es gibt nur leider kein ordentliches Pattern für das Problem, jedenfalls ist mir keins bekannt.

    Man kann es natürlich so einrichten, dass bei der Zerstörung des Strings das Objekt benachrichtigt wird (Observer). Nur ist das übertrieben, wenn man die Lebenszeit des Strings garantieren kann.



  • Ja, brauchen tut mans schon manchmal. Aber trotzdem würde ich hier höllisch aufpassen, vor allem wenn man nicht alleine an dem Code werkelt.



  • Nexus schrieb:

    Man kann es natürlich so einrichten, dass bei der Zerstörung des Strings das Objekt benachrichtigt wird (Observer). Nur ist das übertrieben, wenn man die Lebenszeit des Strings garantieren kann.

    Genau das ist ja mein Problem damit. Eine art Observer-Wrapper oder auch shared_ptr könnte das leisten, aber es ist eben nicht die Lösung. Nicht nur dass es runtime Overhead mitbringt, es packt das Problem auch nicht da wo es sitzt: Die Variable soll einfach garantiert länger leben als die Referenz. Richtig toll wäre z.B. gleich einen Compilerfehler zu generieren, aber ich bezweifle dass das möglich ist. 😞

    @Skym0sh0 Leider braucht man das ziemlich oft.



  • cooky451 schrieb:

    Compilerfehler

    Wie soll das im Allgemeinen gehen?

    std::unique_ptr<std::string> s(new std::string("test"));
    Beispiel b(test);
    
    if (rand()%2)
      s.reset();
    


  • 1.- deine externe string Variable gelöscht wird => deine Klasse auf einmal auf den String zugreifen will, das geht dann aber nicht mehr (logischerweise)

    Wie soll das möglich sein? Einen std::string kannst du nicht einfach zerstören, er müsste aus dem jeweiligen Scope raus. Das erzeugte Objekt würde dann ebenfalls zerstört, da es nach dem std::string erzeugt wurde und somit sich mind. im gleichen, oder "geringerem" Scope befinden muss.

    Von daher sehe ich da überhaupt kein Problem.



  • hthd schrieb:

    Wie soll das möglich sein? Einen std::string kannst du nicht einfach zerstören, er müsste aus dem jeweiligen Scope raus. Das erzeugte Objekt würde dann ebenfalls zerstört, da es nach dem std::string erzeugt wurde und somit sich mind. im gleichen, oder "geringerem" Scope befinden muss.

    simpel:

    std::String* pointerString = new std::string("Hallo");
    Beispiel errorBeispiel(*pointerString);
    delete pointerString;
    errorBeispiel.tueWasMitString();
    

    ein etwas realistischeres Beispiel:

    struct DummyStruct
    { 
      DummyStruct : test("Blabla") {}
      std::string& GetTestString() {return test;}
      std::string test; 
    }
    
    void doSomething()
    {
      DummyStruct* dummy = new DummyStruct; 
      Beispiel errorBeispiel(dummy->GetTestString());
      delete dummy;
      errorBeispiel.tueWasMitString();
    }
    


  • hthd schrieb:

    1.- deine externe string Variable gelöscht wird => deine Klasse auf einmal auf den String zugreifen will, das geht dann aber nicht mehr (logischerweise)

    Wie soll das möglich sein? Einen std::string kannst du nicht einfach zerstören, er müsste aus dem jeweiligen Scope raus. Das erzeugte Objekt würde dann ebenfalls zerstört, da es nach dem std::string erzeugt wurde und somit sich mind. im gleichen, oder "geringerem" Scope befinden muss.

    Von daher sehe ich da überhaupt kein Problem.

    Geht, wenn string oder Beispiel nicht nur auf dem Stack liegen, sondern zum Beispiel per new angelegt wurden oder in sonst einem komischen Speicher liegen.

    void f(){
       string abc("abc");
       throw Beispiel(abc);//komischer Speicher, wo Excpeptions leben
    }
    void g(){
       string abc("abc");
       static Beispiel bsp(abc);//komischer Speicher, wo statische Variablen leben
       bsp.tueWasMitString();
       cout<<abc;
    }
    


  • Hey Jungs,

    ist nicht genau für solche Fälle der shared_ptr da? Ok, da ist Overhead bei, aber da ist auch garantiert, dass ein Objekt solange lebt bis es nicht mehr gebraucht wird.



  • Skym0sh0 schrieb:

    ist nicht genau für solche Fälle der shared_ptr da? Ok, da ist Overhead bei, aber da ist auch garantiert, dass ein Objekt solange lebt bis es nicht mehr gebraucht wird.

    stimmt, in einem solchen Fall wäre ein shared_ptr sicherlich angebracht. WUrde von cooky ja auch schon erwähnt.



  • Jaja, das hab ich auf dem Schirm. Aber er meinte ja, was von wegen ohne shared_ptr lösen. Die Frage ist wieso?

    Ok, das ganze in die Compiletime zu verlegen wäre schon cool, aber weniger möglich, denke ich.



  • Die Gefahr mit den komischen Speicherbereichen ist gar nicht so riesig.
    Hat es Sinn, das ohne shared_pointers zu machen, dann wird es auch so sein, daß man sich kaum eine mißbräuchliche Verwendung einfallen läßt. Also wenn der string vielleicht sowas wie ein socket provider ist, den man bestimmt nicht löscht, solange sockets von ihm leben könnten. Oder irgendwelche Thread-Multiplexer oder das Welt-Objekt im Game.
    ~~Nicht zu vergessen, daß string vermutlich eh copy-on-write drinnen hat, da haben wie die shard_pointer-Kosten ja schon, vielleicht die ausgabe auch übers Beispiel leiten und Kopie des Strings merken.
    ~~
    Und kurzlebige Proxies natürlich.

    ostream& operator<<(ostream& out,Beispiel const& bsp){
       return out<<time(0)<<' '<<bsp.str;
    }
    ...
    logfile<<Beispiel(msg)<<endl;
    


  • volkard schrieb:

    Nicht zu vergessen, daß string vermutlich eh copy-on-write drinnen hat, da haben wie die shard_pointer-Kosten ja schon, vielleicht die ausgabe auch übers Beispiel leiten und Kopie des Strings merken.

    Ist mit C++11 afaik nicht mehr erlaubt.



  • Kellerautomat schrieb:

    volkard schrieb:

    Nicht zu vergessen, daß string vermutlich eh copy-on-write drinnen hat, da haben wie die shard_pointer-Kosten ja schon, vielleicht die ausgabe auch übers Beispiel leiten und Kopie des Strings merken.

    Ist mit C++11 afaik nicht mehr erlaubt.

    das Forum lernt 🙂
    http://www.c-plusplus.net/forum/p2281175#2281175



  • Skym0sh0 schrieb:

    Jaja, das hab ich auf dem Schirm. Aber er meinte ja, was von wegen ohne shared_ptr lösen. Die Frage ist wieso?

    Hab ich doch auch schon geschrieben. Technisch gesehen ist es sinnloser Overhead. Sprachlich gesehen ist es einfach das falsche Konzept: Es existiert keine shared ownership. Der String hat halt einfach in jedem Fall länger zu leben, wenn das nicht der Fall ist, ist das ein Programmierfehler.



  • cooky451 schrieb:

    Der String hat halt einfach in jedem Fall länger zu leben, wenn das nicht der Fall ist, ist das ein Programmierfehler.

    Und wenn nach einem new kein passendes delete folgt, ist das auch ein Programmierfehler. Das ist aber kein Grund, das Klassendesign nicht so vorzunehmen, dass solche Programmierfehler möglichst ausgeschlossen werden.



  • Lies noch mal den ganzen Thread. 🙄



  • cooky451 schrieb:

    Der String hat halt einfach in jedem Fall länger zu leben, wenn das nicht der Fall ist, ist das ein Programmierfehler.

    Hier wäre eine Mischung von string_reference und initializer_list angebracht:

    template <typename T> granted_lifetime; // Klasse in der Standardbibliothek mit Compilermagie
    Beispiel::Beispiel(granted_lifetime<std::string>);
    
    // ...
    string abc("abc");
    Beispiel bsp1(abc); // abc und bsp sind im automatischen Speicher -> abc konvertiert implizit zu granted_lifetime
    Beispiel *bsp2 = new Beispiel(abc); // bsp liegt liegt im dynamischen Speicher -> Compilerfehler
    
    shared_ptr<string> def = make_shared<string>("abc");
    Beispiel bsp3(def); // shared_ptr konvertiert *immer* zu granted_lifetime
    Beispiel *bsp4 = new Beispiel(abc); // dito
    
    unique_ptr<string> ghi = new string("abc");
    Beispiel bsp5(ghi.get()); // Pointer sind unsicher => Compilerfehler
    Beispiel bsp6(grant_lifetime(ghi.get())); // Garantiere explizit
    


  • cooky451 schrieb:

    Lies noch mal den ganzen Thread. 🙄

    ich habe den Thread verfolgt.

    Wenn man das so programmiert, dann muss der string mindestens so lange Leben, wie das Objekt, das stimmt. Und weil diese Einschränkung leicht umgangen werden kann, sollte überdenken, ob das Design so wirklich sinnvoll ist.



  • @kugelglas Die granted_lifetime Klasse ist nur leider unmöglich zu schreiben denke ich, auch mit Compilermagie.

    Eine Möglichkeit wäre noch eine Klasse zu schreiben in die eben nicht implizit konvertiert wird. Die wäre dann einfach nur Deko und würde gar nichts machen, aber man ist gezwungen von außen kenntlich zu machen, was in der Klasse passiert.

    @daddy_felix Nur weil etwas funktioniert, muss es nicht gut sein. Und wenn du den ganzen Thread gelesen hast, war dein Kommentar sinnlos.



  • cooky451 schrieb:

    @kugelglas Die granted_lifetime Klasse ist nur leider unmöglich zu schreiben denke ich, auch mit Compilermagie.

    Die wichtigsten Spezialfälle könnte man IMHO schon abdecken. Und in die anderen Fällen (welche dann auch für den Code-Leser nicht offensichtlich sind) muss man halt explizit grant_lifetime() aufrufen.

    Und selbst jetzt mit C++11, wenn die Klasse nur Deko wäre, fände ich sie nicht einmal so schlecht. (Die Frage ist nur, wie man sie in die Klasse bekommt, sie darf halt weder Copyable noch Movable sein; wahrscheinlich mit einem weiteren Aufruf von grant_lifetime: s(grant_lifetime(granted)).)


Anmelden zum Antworten