Fragen zu Zeigern in Klassen



  • Égal ob man nun Pointer braucht oder nicht, es ist 2014, da verwendet man keine Raw-Pointer mehr. Wenn dann bitte std::unique_ptr<> oder wenn es unbedingt sein muss std::shared_ptr<>. Wenn das Buch das du liest noch normale Pointer lehrt, lege es bitte in die Ecke und kaufe ein richtiges Buch über C++11.



  • SeriousITGuy schrieb:

    Égal ob man nun Pointer braucht oder nicht, es ist 2014, da verwendet man keine Raw-Pointer mehr.

    Das mag für besitzende Pointer richtig sein, so generell wie du es sagst aber sicher nicht.



  • SeriousITGuy schrieb:

    Égal ob man nun Pointer braucht oder nicht, es ist 2014, da verwendet man keine Raw-Pointer mehr. Wenn dann bitte std::unique_ptr<> oder wenn es unbedingt sein muss std::shared_ptr<>. Wenn das Buch das du liest noch normale Pointer lehrt, lege es bitte in die Ecke und kaufe ein richtiges Buch über C++11.

    Nö, auch mit C++11 oder C++14 darf man ruhig noch rohe Zeiger benutzen, solange es keine besitzenden Zeiger sind.



  • Natürlich kann man heute auch Raw-Pointer verwenden, aber ist es sinnvoll?

    Stichwort "Strong Ownership". Korrekt angewendetes "Strong Ownership" (inclusive RAII) macht Pointer zu weiten Teilen komplett überflüssig. Benötigt man dennoch Pointer (weil ein Objekt nicht zur Initialisierungszeit seines besitzenden Objekts initialisiert werden kann) ist ein Smart-Pointer immer die erste Wahl. (Früher scoped_ptr, heute unique_ptr)

    Jedes new benötigt ein delete. Selbst das einfachste Programm welches Raw-Pointer verwendet kann nicht ohne großen Aufwand zu betreiben exception-safe programmiert werden. Smart-Pointer sind automatisch exception safe. Kombiniert mit RAII sind memory-leaks ein Ding der Vergangenheit. Siehe hierzu http://www.bromeon.ch/articles/raii.html

    Ich kann mir nur noch einen legitimen Gebrauch von Raw-Pointern vorstellen, die Implementierung eines eigenen Smart-Pointer Container.
    Hier bin ich aber gerne offen für (legitime) weitere Beispiele 😉



  • SeriousITGuy schrieb:

    Natürlich kann man heute auch Raw-Pointer verwenden, aber ist es sinnvoll?

    Ja. Und zwar wenn man kein Objekt besitzt, sondern nur auf eines verweist (und dieser Verweis auch noch geändert werden kann, was mit Referenzen nicht funktioniert). Eine Ausnahme ist nur dann gegeben wenn man wirklich keine Aussage zur Lebenszeit machen kann (in dem Fall sind shared_ptr ausnahmsweise sinnvoll).

    Und dies gilt auch unter C++11 wie wohl auch noch in alle C++-Ewigkeit.



  • Es geht nicht um besitzende Zeiger, sondern um Verweise.

    Sowas z.B. (obwohl es ein dämliches Beispiel ist)

    auto foo = std::make_unique<Foo>();
    // ...
    Foo * pF = foo.get();
    // ...
    foo->stuff();
    pF->stuff();
    // ...
    // delete *foo implizit druch unique_ptr
    

    pF ist zwar ein roher Zeiger, hat aber keinerlei Besitzverantwortlichkeiten. Und wer dann sowas wie delete pF schreibt ist selbst schuld.

    Edit: Anwendungsbeispiele sind z.B. klassenhierarchien, wo die oberste Klasse eben den Unique_ptr hält (oder sogar ein "normales" Objekt) und die Kindklassen eben einen Verweis darauf brauchen:

    #include <iostream>
    #include <vector>
    #include <memory>
    #include <iterator>
    #include <algorithm>
    
    class Foo
    {
    public:
    	void foo() const
    	{
    		std::cout << "Hallo" << "\n";
    	}
    };
    
    class Child
    {
    	Foo * m_foo;
    
    public:
    	explicit Child(Foo * pf)
    	 : m_foo(pf)
    	{}
    
    	void bar() const
    	{
    		std::cout << this << ".- ";
    		this->m_foo->foo();
    	}
    };
    
    class Parent
    {
    	std::unique_ptr<Foo> m_foo;
    	std::vector<Child> m_children;
    
    public:
    	explicit Parent(std::size_t n)
    	 : m_foo(new Foo())//(std::make_unique<Foo>())
    	{
    		std::generate_n(std::back_inserter(this->m_children), n, [&](){return Child{m_foo.get()};});
    	}
    
    	void print() const
    	{
    		std::cout << this << ".-> ";
    		this->m_foo->foo();
    		for(auto const& c : this->m_children)
    		{
    			c.bar();
    		}
    	}
    };
    
    int main()
    {
    	Parent p{5};
    
    	p.print();
    
    	return 0;
    }
    

    Und da die Kinder automatisch durch parent gelöscht werden, müssen die sich auch nicht um den Speicher von Foo kümmern. Die Verantwortlichkeit hat auch parent



  • Das erste Beispiel ist wohl nur konstruiert um ein Gegenbeispiel zu bringen oder? Was macht es darin für einen Sinn Foo * pF = foo.get(); zu definieren um danach genau das gleiche zu machen wie mit dem std::unique_ptr?

    Das zweite Beispiel erscheint logisch, ich habe aber in meinen bisherigen Projekten eine solche Klassen-Architektur nicht gesehen. Nochmal Stichwort "Strong Ownership". Man findet in jeder Klassen-Architektur immer einen Besitzer für ein Objekt. Müssen andere darauf zugreifen, wenn dann mittels Referenzen.

    In der Theorie lass ich mir das ja eingehen, aber in der Praxis habe ich seit der Existenz von smart pointern (genauer shared/unique) keinen Gebrauch mehr von Raw-Pointern machen müssen.



  • Referenzen verhindern Move Semantics.



  • SeriousITGuy schrieb:

    Das erste Beispiel ist wohl nur konstruiert um ein Gegenbeispiel zu bringen oder?

    Nein, und das hat auch nichts mit "Strong Ownership" zu tun. Letzteres ist sinnvoll hat aber nichts mit Verweisen zu tun, wo Referenzen nun einmal nicht immer möglich sind (Referenzen können sich nicht ändern und müssen zwangsweise im Konstruktor gesetzt sein).

    SeriousITGuy schrieb:

    In der Theorie lass ich mir das ja eingehen, aber in der Praxis habe ich seit der Existenz von smart pointern (genauer shared/unique) keinen Gebrauch mehr von Raw-Pointern machen müssen.

    Schön für dich, aber ich selbst bin ein Verfechter von Smartpointern, kann dir aus der Praxis aber sagen das man recht häufig nicht besitzende Zeiger verwendet, und nicht immer auf Referenzen ausweichen kann.



  • @SerilusITGuy:
    Nicht alle Objekte liegen auf dem Heap, manchmal braucht man Verweise auf Stackobjekte...


Anmelden zum Antworten