Pointer/Referenzen kleine Frage



  • Ich möchte den String, der dem Konstruktor der Klasse Beispiel übergeben wird nicht kopieren, sondern weiterhin auf den std::string der auch im "Hauptprogramm" intialisiert wurde zugreifen.

    class Beispiel {
    public:
      Beispiel(std::string &str) { mStr = &str; }
      void tueWasMitString() { *mStr += "AAA"; }
    
    private:
      std::string *mStr;
    };
    
    int main() {
    
      std::string test = "BBB";
      Beispiel b(test);
      b.tueWasMitString();
    
      std::cout << test; // soll "BBBAAA"
    }
    

    Hier bekomme ich aber die Meldung vom Compiler in Zeile 4 "Der Ausdruck muss einen Klassentyp aufweisen", wieso?





  • Wahrscheinlich hast du ein include vergessen (wenn du mehrere Files hast, ansonsten kann man den Fehler hier nicht sehen).



  • Wobei das, was du vorhast auch nicht so ganz super ist.
    Die hast eine Klasse, die von der Lebensdauer einer anderen Klasse direkt abhängt. Das heisst es könnte schnell mal passieren, dass
    1.- deine externe string Variable gelöscht wird => deine Klasse auf einmal auf den String zugreifen will, das geht dann aber nicht mehr (logischerweise)
    2.- deine Klasseninternas können von aussen geändert werden -> das widerspricht dem Paradigma der Kapselung



  • @Skym0sh0 Es gibt nur leider kein ordentliches Pattern für das Problem, jedenfalls ist mir keins bekannt. Wobei Problem 2 eher nebensächlich ist, das erste zu lösen (ohne shared_ptr!) wäre schon toll.



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


Anmelden zum Antworten