Problem mit Copy Constructor - wie lösen?



  • Hm. Um was für eine Funktion handelt es sich bei f im Originalbeispiel? Musst du wirklich eine neue Instanz der Klasse zurückgeben? Und diese sollte polymorphes Verhalten aufweisen?



  • Im Original ist C1 eine Basisklasse für Trajektorien, also eine Approximation für mathematische Funktionen. C2 ist dann eine Implementierung (Polynom / Spline). f() entspricht der Ableitung dieser Trajektorie.



  • Okay. Grundsätzlich musst du wohl Speicher dynamisch anfordern, da ja im Voraus nicht klar ist, was f() für einen (dynamischen) Typen zurückgibt.

    C1* C1::f() const // <- verändert Methode etwas an this?
    {
        C1* NewC;
        if (...)
            NewC = new C2(...);
        else
            NewC = new C3(...);
        return NewC;
    }
    

    So hast du allerdings noch das Problem, wer den Speicher freigibt. Du kannst zum Beispiel eine analoge Freigabefunktion benutzen (eventuell wären freie Funktionen, auch für die Objekterstellung, die bessere Wahl):

    C1* CreateC() const;
    void DestroyC(C1* c) const;
    

    Oder du nutzt gleich C++'s Möglichkeiten der RAII und gibst einen Smart Pointer zurück, zum Beispiel boost::shared_ptr ( std::tr1::shared_ptr ), wobei hier eigentlich std::auto_ptr reichen sollte. Wenn du diesen nicht kennst, schau mal auf www.cplusplus.com nach.

    std::auto_ptr<C1> f() const;
    

    Achja, vergiss übrigens den virtuellen Destruktor nicht, sonst werden deine polymorphen Objekte nicht richtig zerstört.



  • Nein, die Methode macht an this nichts.Freie Funktionen sind eher unschön, wahrscheinlich werde ich es einfach mal mit den Pointern probieren... Muss ich mich halt sauber ums freigeben kümmern, wofür gibts valgrind 😉

    Virtueller Destructor war natürlich drinnen...

    Den auto_ptr kannte ich noch nicht, das ist klasse! Vielen Dank, muss mich da aber erst mal an den Gedanken gewöhnen, das ist ja schon fast Java 😃



  • luke-r2d2 schrieb:

    wahrscheinlich werde ich es einfach mal mit den Pointern probieren... Muss ich mich halt sauber ums freigeben kümmern, wofür gibts valgrind 😉

    Bei Smart Pointers wie std::auto_ptr musst du dich eben nicht ums Freigeben kümmern. Und der Overhead sollte vertretbar sein...

    luke-r2d2 schrieb:

    Freie Funktionen sind eher unschön

    Das würde ich in C++ nicht sagen. Geh mal rüber zu den C'lern, die werden dich umbringen. 😃

    Nein, im Ernst. Ich finde, wenn eine Funktion keinen direkten Zugriff auf die Member braucht oder aus sonstigen Gründen auch genauso gut ausserhalb der Klasse stehen kann, in diesem Falle eine globale Funktion besser und schöner. Es bringt einem nichts, sie in der Klasse zu haben. Nur weil das von gewissen Leuten nicht als "objektorientiert" angesehen wird, ist für mich kein Grund. Allein dass man die Funktion in die Klasse steckt, macht das Ganze nicht objektorientierter, geschweige denn eleganter.

    Freie Funktionen können genauso gut zur Schnittstelle einer Klasse gehören. Beispiel wären bei einer Vektorklasse Funktionen zur Länge. Ich finde es auch syntaktisch schöner, zu schreiben:

    float Length = Norm(Vector1 + 2 * Vector2);
    

    schöner als:

    float Length = (Vector1 + 2 * Vector2).Norm();
    

    Und nach Java richten würde ich mich in C++ erst recht nicht. Dann hätte man nämlich

    float length = (vector1.add(vector2.mult(2))).norm();
    

    :p



  • Hmja, okay 😉

    Den Overhead von den smart pointers kann ich noch nicht einschätzen... Kann nur sagen, dass das Programm mal ein paar Tage auf nem Superrechner laufen wird, da machen auch Kleinigkeiten viel aus 😉 Wobei ich nicht denke, dass das selber Freigeben ein so großes Problem sein sollte, ich benutze das zum Glück nur selten.

    Freie Funktionen können schöner sein - müssen aber nicht. Ich bevorzuge in dem Fall

    traj->getSubTraj(i, n)->derivative()->derivative()->calculatePoint(t)
    

    (Freigabe erfolgt in traj, ich speichere jetzt einfach die Ableitungen direkt ab - spart auch Rechenzeit)

    vor irgendwelchen freien Funktionen. Ich denke, das ist Einstellungssache - ein weiterer Vorteil ist, dass ich z.B. verschiedene derivative() Funktionen in meinem Programm habe, und dann wäre ein derivativeSpline(), derivativeVector(), etc eher unschön. Mit Klassen ist das halt auch sortiert...



  • Ja, es kommt ganz auf den Anwendungsfall drauf an. Nur würde ich mich hüten, generell etwas gegen freie Funktionen zu haben. 😉

    Zu dem Selber Freigeben: Mach es wenn schon so, dass eine Funktion im gleichen Scope für die Freigabe verantwortlich ist. Und gewöhn dir besser nicht an, sowas zu machen:

    C* t = bla.CreateC();
    delete t;
    

    Also direkt freigeben. Denn damit machst du dich von der Implementierung der Anforderungsfunktion abhängig. Was, wenn CreateC() nicht mehr mit new anfordert? Oder wenn es etwas zusätzlich macht, das beim Freigeben wieder rückgängig gemacht werden soll? Auch wenn es hier vielleicht nicht so relevant ist, würde ich trotzdem schon darauf achten, diesbezüglich konsistent zu bleiben und eine entsprechende Freigabefunktion in der Klasse anzubieten. Du verlierst dadurch nichts.

    Und was Smart Pointers angeht: Die können sich schnell einmal lohnen. Gerade wenn man eine Funktion über mehrere Wege verlassen kann (z.B. auch durch Exceptions), muss man enorm viel Code schreiben, um in jedem Fall für eine Freigabe zu sorgen. Bei Smart Pointers hat man das gratis - ein auto_ptr gibt den Speicher frei, wenn er selber zerstört wird - also normalerweise beim Verlassen des Scopes. Und auto_ptr ist ein verhältnismässig leichtgewichtiger Smart Pointer. Ich weiss nicht, ob der Overhead überhaupt spürbar ist. Und falls doch, lieber keine Memory Leaks als ein 0.0001% langsameres Programm. 🙂



  • Hmm, okay, danke auf jeden Fall für nen Tipp mit den smart pointers 😉 Muss ich mir wirklich mal genauer anschaun, bzw verstehen, was genau ich damit machen darf 😛

    Und mit der eigenen Freigabefunktion - das mein ich ja 😉 Also den Pointer in der Klasse abspeichern und beim Zerstören mit vernichten 😛 (oder eben über Funktionen)...



  • luke-r2d2 schrieb:

    Und mit der eigenen Freigabefunktion - das mein ich ja 😉 Also den Pointer in der Klasse abspeichern und beim Zerstören mit vernichten 😛 (oder eben über Funktionen)...

    Dann meinst du aber doch was anderes. Wieso soll der Pointer in der Klasse abgespeichert werden? Der ist ja unabhängig von der Lebenszeit des Objekts, das ihn erstellt. Zumindest ist es gefährlich, einen Pointer anzufordern, von dem man nicht weiss, wann er freigegeben wird. Damit hast du wieder die erwähnte Inkonsistenz.

    Was ich meinte, ist, dass derjenige, der den Speicher anfordert, auch wieder für dessen Freigabe verantwortlich sein sollte. Also in deinem Beispiel der Aufrufer der Trajektorien-Erstellungsfunktion und eben nicht die Klasse, die die Erstellungsfunktion bereitstellt.



  • Hmm, es ist aber sinnvoll, in einer Trajektorie die Ableitung abzuspeichern, wenn man die öfter braucht 😉


Anmelden zum Antworten