Map mit Template Typen



  • Ja ich wollte mich jetzt nicht von dem unique_ptr abhängig machen.
    Das klingt jetzt vielleicht doof, aber ich mag es nicht mit dem zu arbeiten.
    Ich kümmer mich lieber selbst um den Speicher.



  • Ja ich wollte mich jetzt nicht von dem unique_ptr abhängig machen.

    Was bitte soll das heißen?

    Das klingt jetzt vielleicht doof, aber ich mag es nicht mit dem zu arbeiten.

    Das klingt tatsächlich doof, den der unique_ptr ist eigentlich Pflicht in modernem C++.

    Ich kümmer mich lieber selbst um den Speicher.

    Hier ist das vielleicht noch irgendwie in Ordnung, aber glaub mir, diese Devise solltest du ganz schnell vergessen.



  • DuffCola schrieb:

    Das klingt jetzt vielleicht doof, aber ich mag es nicht mit dem zu arbeiten.

    Nein, das klingt ganz und gar nicht doof, das ist doof! Benutze RAII wann immer es geht!



  • Wahrscheinlich meint er, weil er rohe Pointer benutzt holt er Geschwindigkeit raus.
    Aber er weis nicht, was er macht daher...



  • Wahrscheinlich meint er, weil er rohe Pointer benutzt holt er Geschwindigkeit raus.

    Prinzipiell verursacht ein unique_ptr keinen Overhead. Er hat dieselbe Größe wie der Zeiger, hält keine anderen Member*, und alle Operationen die er implizit erledigt, muss man auch selbst erledigen.

    Ich habe es nicht getestet, bin mir aber sehr sicher dass der Overhead absolut minimal sein dürfte.

    *: Nein, es muss ja technisch gesehen noch den Deleter halten. S. Seite 2.



  • std::unique_ptr ist genauso ein Teil der Standardbibliothek wie std::map, sogar deutlich leichter zu implementieren. Also wenn du unique_ptr nicht willst weil dein Spiel auch auf einer Armbanduhr laufen soll, dann bitte auch std::map selbst implementieren. 😉



  • @DuffCola (aber auch Sone und cooky451): unique_ptr hat nicht minimalen Overhead, sondern rein gar keinen.

    Es ist tatsächlich nicht klug, in C++ Speicher manuell verwalten zu wollen. Du machst dir das Leben nur schwerer, ohne irgendeinen Vorteil davon zu haben. Die Leute in diesem Buch haben sich schon was überlegt 😉



  • unique_winner schrieb:

    unique_ptr hat nicht minimalen Overhead, sondern rein gar keinen.

    Das bezweifle ich. (Ich weiß es wie gesagt nicht, schätze aber, dass in bestimmten Situationen ein Funktionsaufruf o.ä. Overhead verursachen könnte. )

    und cooky451

    Was hat cooky damit zu tun? Er hat keine Aussage über den Overhead gemacht.



  • Die Memberfunktionen von unique_ptr werden komplett geinlined, da ist absolut 0 Overhead.



  • Nein, tatsächlich muss unique_ptr sogar einen zweiten Member halten (bzw. irgendwie auf ihn Verweisen): Den Deleter.

    Die Memberfunktionen von unique_ptr werden komplett geinlined, da ist absolut 0 Overhead.

    Das wird schon stimmen. Auch die vom Deleter? Sicher, auch die. Dann vielleicht tatsächlich kein Overhead.


  • Mod

    Sone schrieb:

    Nein, tatsächlich muss unique_ptr sogar einen zweiten Member halten (bzw. irgendwie auf ihn Verweisen): Den Deleter.

    Was im Falle des hier nur interessanten default_delete<T> keinen Overhead darstellt (da leere Klasse), eine vernünftige Implementierung unterstellt.



  • unique_winner schrieb:

    @DuffCola (aber auch Sone und cooky451): unique_ptr hat nicht minimalen Overhead, sondern rein gar keinen.

    Naja, unique_ptr kann schon Overhead haben.

    Beispielcode:

    namespace {
      template <typename T>
      struct unique_ptr {
        T* data;
        unique_ptr(T* data) : data(data) {}
        unique_ptr(unique_ptr&& o) : data(o.data) { o.data = 0; }
        ~unique_ptr() { delete data; }
      };
    
      unique_ptr<int> make_unique(int v)
      {
        return unique_ptr<int>(new int(v));
      }
    
      int* make_ptr(int v)
      {
        return new int(v);
      }
    }
    
    void f()
    {
      auto i = make_unique(0);
    }
    
    void g()
    {
      auto i = make_ptr(0);
      delete i;
    }
    

    gcc mit -O2 -fno-elide-constructors liefert:

    f:

    .cfi_startproc
    	pushq	%rbx
    	.cfi_def_cfa_offset 16
    	.cfi_offset 3, -16
    	movl	$4, %edi
    	call	_Znwm
    	xorl	%edi, %edi
    	movl	$0, (%rax)
    	movq	%rax, %rbx
    	call	_ZdlPv
    	xorl	%edi, %edi
    	call	_ZdlPv
    	movq	%rbx, %rdi
    	popq	%rbx
    	.cfi_def_cfa_offset 8
    	jmp	_ZdlPv
    	.cfi_endproc
    

    g:

    .cfi_startproc
    	subq	$8, %rsp
    	.cfi_def_cfa_offset 16
    	movl	$4, %edi
    	call	_Znwm
    	movl	$0, (%rax)
    	movq	%rax, %rdi
    	addq	$8, %rsp
    	.cfi_def_cfa_offset 8
    	jmp	_ZdlPv
    	.cfi_endproc
    

    Bei einem gcc mit -fno-elide-constructors sieht allerdings beides gleich aus.

    Sone schrieb:

    Nein, tatsächlich muss unique_ptr sogar einen zweiten Member halten (bzw. irgendwie auf ihn Verweisen): Den Deleter.

    Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.



  • Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.

    Ich kenne C++. Laut Standard muss unique_ptr einen Deleter halten. Wenn dieser 0 Bytes groß ist, wie bei default_deleter , wird er als Member auch wegoptimiert.



  • shared_disadvantage schrieb:

    Stichwort EBO.

    Bzw. optimal wäre tuple<T*, Deleter> .



  • Sone schrieb:

    Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.

    Ich kenne C++. Laut Standard muss unique_ptr einen Deleter halten. Wenn dieser 0 Bytes groß ist, wie bei default_deleter , wird er als Member auch wegoptimiert.

    Basiswissen C++: Member dürfen nicht wegoptimiert werden.



  • shared_disadvantage schrieb:

    Stichwort EBO.

    Bzw. optimal wäre tuple<T*, Deleter> .

    Ja, so macht es der GCC.



  • shared_disadvantage schrieb:

    Sone schrieb:

    Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.

    Ich kenne C++. Laut Standard muss unique_ptr einen Deleter halten. Wenn dieser 0 Bytes groß ist, wie bei default_deleter , wird er als Member auch wegoptimiert.

    Basiswissen C++: Member dürfen nicht wegoptimiert werden.

    Eine Klasse muss mindestens einen Byte groß sein, da jedes Objekt eine Adresse haben muss.
    Und das wird bei Membern nicht wegoptimiert, wenn diese nicht "genutzt" werden? Das heißt, bei Basisklasen, die auch Subobjekte sind, darf man das - hier nicht?


  • Mod

    shared_disadvantage schrieb:

    shared_disadvantage schrieb:

    Stichwort EBO.

    Bzw. optimal wäre tuple<T*, Deleter> .

    Kaum. Genau das würde nämlich EBO unterschlagen. Tupelelemente können nicht durch unmittelbare Vererbung implementiert werden (das hätte sonst bestimmte Einschränkungen zur Folge, so könnten POD-Tuplemember nicht mehr per memcpy angefasst werden).



  • Das klingt tatsächlich doof, den der unique_ptr ist eigentlich Pflicht in modernem C++.

    Das ist doch Quatsch. Hoer doch mal mit dem Dogma auf!

    Benutze RAII wann immer es geht!

    Macht er doch, er will sie im Destruktor freigeben ...

    Wahrscheinlich meint er, weil er rohe Pointer benutzt holt er Geschwindigkeit raus.
    Aber er weis nicht, was er macht daher...

    Aus der Luft gegriffene Unterstellung.

    Den Deleter.

    Der Deleter steckt normalerweise im Typ und nicht im Objekt. 2 unique_ptr vom gleichen Typ koennen keinen unterschiedlichen deleter haben.



  • Das heißt, bei Basisklasen, die auch Subobjekte sind, darf man das - hier nicht?

    Hmm. Die Frage sollte sich wohl von selbst beantworten. Natürlich können die nicht optimiert werden...

    unique_ptr vom gleichen Typ koennen keinen unterschiedlichen deleter haben.

    Nein der Typ ihres Deleters kann nicht unterschiedlich sein, und? Was heißt das jetzt im Bezug auf den Member?

    Das ist doch Quatsch.

    Was ist Quatsch? Ich rede offensichtlich von den Fällen, wo ein roher Zeiger ein Objekt besitzt...


Anmelden zum Antworten