Frage zu Templates



  • hallöchen Leute 😃

    also, ich programmiere gerne mal Sachen nach, die es schon gibt, einfach um daraus zu lernen...
    allerdings mache ich das nach Möglichkeit ohne mir die Original-Implementation azuschauen,
    nur das Interface.

    am liebsten sind mir das STL und boost, da kann man viel draus lernen.

    ok, jetzt zu meinem Problem:
    ich habe 6 schlaue Zeigerklassen xD, also scoped_..., shared_... und auto_...
    jeweils für pointer und array.

    als letztes hab ich jetzt angefangen, diese mit Allocator-Klassen auszustatten.
    das klappt bei den sopeds auch ganz gut:

    template <class _Ty, class _Allocator = new_allocator<_Ty>>
    class scoped_pointer : private noncopyable
    {
    private:
    	_Ty* m_pPtr;
    	_Allocator m_Allocator;
    public:
    	scoped_pointer(void) : m_pPtr(0) {}
    	explicit scoped_pointer(_Ty* p) : m_pPtr(p) {}
    	~scoped_pointer(void) { if (m_pPtr) m_Allocator.release(m_pPtr, 1); }
    	_Ty* get(void) const { return m_pPtr; }
    	_Ty* operator -> (void) const { return m_pPtr; }
    	_Ty& operator * (void) const { return *m_pPtr; }
    };
    
    template <class _Ty, class _Allocator = new_allocator<_Ty>>
    class scoped_array : private noncopyable
    {
    private:
    	_Ty* m_pArray;
    	_Allocator m_Allocator;
    public:
    	scoped_array(void) : m_pArray(0) {}
    	explicit scoped_array(_Ty* p) : m_pArray(p) {}
    	~scoped_array(void) { if (m_pArray) m_Allocator.release(m_pArray, 2); }
    	_Ty* get(void) const { return m_pArray; }
    	_Ty* operator -> (void) const { return m_pArray; }
    	_Ty& operator * (void) const { return *m_pArray; }
    	_Ty& operator [] (int index) { return m_pArray[index]; }
    };
    

    das Problem ist jetzt bei den anderen...

    also hier mal meine auto_pointer-Klasse:

    template <class _Ty>
    class auto_pointer
    {
    private:
    	_Ty* m_pPtr;
    public:
    	auto_pointer(void) : m_pPtr(0) {}
    	explicit auto_pointer(_Ty* p) : m_pPtr(p) {}
    	auto_pointer(auto_pointer<_Ty>& src) : m_pPtr(src.m_pPtr) { src.m_pPtr = 0; }
    	template <class _Other>
    	auto_pointer(auto_pointer<_Other>& src) : m_pPtr((_Ty*)(src.m_pPtr)) { src.m_pPtr = 0; }
    	~auto_pointer(void) { if (m_pPtr) delete m_pPtr; }
    	_Ty* get(void) const { return m_pPtr; }
    	auto_pointer<_Ty>& operator = (const auto_pointer<_Ty>& src) { if (m_pPtr) delete m_pPtr; m_pPtr = src.m_pPtr; src.m_pPtr = 0; return *this; }
    	template <class _Other>
    	auto_pointer<_Ty>& operator = (const auto_pointer<_Other>& src) { if (m_pPtr) delete m_pPtr; m_pPtr = (_Ty*)(src.m_pPtr); src.m_pPtr = 0; return *this; }
    	_Ty* operator -> (void) const { return m_pPtr; }
    	_Ty& operator * (void) const { return *m_pPtr; }
    };
    

    ich wäre sehr dankbar, wenn mir jemand an dieser Klasse mal erklären, wie was wo geändert werden muss, damit das klappt.

    für die restlichen Klassen kann ich das ja dann selber noch machen.

    mein größtes Problem liegt eben bei sowas:

    template <class _Other>
    	auto_pointer<_Ty>& operator = (const auto_pointer<_Other>& src) { if (m_pPtr) delete m_pPtr; m_pPtr = (_Ty*)(src.m_pPtr); src.m_pPtr = 0; return *this; }
    

    wie änder ich dass, damit das mit dem allocator funktioniert?

    schonmal danke im voraus

    MfG DrakoXP



  • Das ist aber unschön ...

    if (m_pPtr) delete m_pPtr;
    

    ... doppelt gemoppelt. delete m_pPtr reicht.

    m_pPtr = (_Ty*)(src.m_pPtr); src.m_pPtr = 0;
    

    Function-Style-Cast ... nimm C++-Style cast (reinterpret_cast, static_cast, dynamic_cast, const_cast usw.)
    Eigtl. allokierst du da ja nichts. Von d.h. ka wie du da einen Allocator nutzen willst 😉



  • Bezeichner mit einem Unterstrich (_) gefolgt von einem Großbuchstaben sind reserviert für den Kompiler. Solange du kein Kompilerbauer bist, solltest du sie also nicht verwenden, denn Bill Gates kann jeder Zeit entscheiden, dass sein Kompiler für jede Übersetzungseinheit sowas einbindet:

    #define _Ty Bill Gates ist super!!!11elf
    

    Nein, wirklich. Vor allem solche Bezeichner wie _Ty und _Allocator sind nicht gerade so einzigartig, dass man sich darauf verlassen kann, dass sie eine Kompiler nie für irgendwas benutzt.

    Gruß
    Don06



  • so, also erstmal hat keiner von euch beiden meine Frage beantwortet -.-

    und zweitens:

    ein Allocator hat ebenfalls eine Funktion zum freigeben des Speichers, und genau
    dafür braucht ihn mein Zeiger.

    es wäre nähmlich blöd, wenn jemand so einen scoped_pointer mit einem Zeiger füttert, der durch LocalAlloc, oder malloc oder so alloziiert wurde.
    dann würde dieser nämlich auch mit delete freigegeben, was ja nicht geht.

    deshalb der Allocator, so kann man das ganze gegebenenfalls austauschen.

    was die Sache mit dem Unterstrich usw. angeht:
    die STL macht es so, also mach ich es auch so,
    zudem beantwortet das in keiner Weise meine eigentliche Frage


  • Mod

    (D)Evil schrieb:

    Das ist aber unschön ...

    if (m_pPtr) delete m_pPtr;
    

    ... doppelt gemoppelt. delete m_pPtr reicht.

    Es genügt, ist aber keineswegs doppelt gemoppelt. Und gerade im Zusammenhang mit auto_ptr wahrscheinlich sogar effizienter (die Idee ist, dass delete (T*)0 möglicherweise beobachtbar ist (praktisch) und folglich nicht wegoptimiert werden kann, wenn wir geleerte auto_pointer (aus Funktionsrückgaben u.ä.) zerstören, mit einem if davor ist das trivial zu erreichen. Also: je nach Zusammenhang wird so möglicherweise effizienterer Code erstellt und jedenfalls ist es (in meinen Augen) logisch klarer - es ist doch unmöglich, etwas zu löschen, das nicht da ist.

    DrakoXP schrieb:

    ein Allocator hat ebenfalls eine Funktion zum freigeben des Speichers, und genau
    dafür braucht ihn mein Zeiger.

    Also willst du einen eigenen Deleter, nicht einen vollständigen Allokator. Dafür gibt es verschiedene Möglichkeiten, shared_ptr bietet ein recht ausgereiftes System. Prinzipiell kann man das so gestalten:

    template <typename T>
    class scoped_pointer
    {
    public:
        typedef T value_type;
    
        scoped_pointer()
            : p_(), deleter_()
        {}
        explicit scoped_pointer(value_type* p)
            : p_( p ), deleter_()
        {}
        // mit Allokator
        template<typename A>
        scoped_pointer(value_type* p, A allocator)
            : p_( p ), deleter_( &alloc_deleter< A > )
        {}
        // mit Deallokationsfunktion
        template<typename U, typename V>
        scoped_pointer(value_type* p, U del_fun(V*))
            : p_( p ), deleter_( &fun_deleter< U, V > ), del_func_( reinterpret_cast< del_func_type >( del_fun ) )
        {}
        ~scoped_pointer()
        {
            if ( p_ != NULL )
                deleter_ != NULL ? deleter_( p_, del_func_ ) : delete p_;
        }
        value_type* get() const { return p_; }
        value_type& operator*() const { return *get(); }
        value_type* operator->() const { return get(); }
    private:
    // nocopy
        scoped_pointer(scoped_pointer&);
        void operator=(scoped_pointer&);
        typedef void deleter_type(T*);
        typedef void del_func_type();
        value_type* p_;
        deleter_type* deleter_;
        del_func_type* del_func_; // um den Zeiger zur eigentlichen Deallokationsfunktion zu speichern, benutzen wir hier irgendeinen Funktionszeigertyp
    // es ist garantiert, dass reinterpret_cast hin und zurück möglich ist
        typename<typename A>
        friend void alloc_deleter(T* p, del_func_type)
        {
            p->~T();
            A allocator; // alle Allokatoren gleichen Typs sind äquivalent
            allocator.deallocate( p );
        }
        typename<typename U, typename V>
        friend void fun_deleter(T* p, del_func_type del_func)
        {
            p->~T();
            reinterpret_cast< U(*)(V*) >( del_func )( p );
        }
    };
    
    ...
    scoped_ptr<int>(std::malloc(sizeof(int)),std::free);
    

    völlig ungetest. shared_ptr ist hier flexibler, indem auch Funktionsobjekte als Deleter in Frage kommen, dass bekommt man aber ohne zusätzliche dynamische Speicheranforderung kaum in den Griff - es sei denn, man macht die Deallokationsstrategie Teil des Typs; für scoped_ptr ist das ggf. akzeptabel, bei komplizierter Zeigern aber nicht unbedingt wünschenswert.

    DrakoXP schrieb:

    was die Sache mit dem Unterstrich usw. angeht:
    die STL macht es so, also mach ich es auch so,

    Nein, macht sie nicht. Und selbst wenn es so wäre, solltest du den Hinweis akzeptieren. Es ist schlicht und ergreifend unzulässig, und ganz nebenbei potthässlich.

    Was auto_pointer betrifft - den solltest du erst einmal ohne zusätzliche Spielereien implementieren. Bereits das ist nämlich ganz und gar nicht trivial.



  • okok, das war schonmal hilfreicher...

    mal sehen, wenn ich mal Lust habe werd ich dann mal die Template-Argumente umbenennen xD

    naja, also für die Klasse scoped_pointer hatte ich das ja schon soweit, dass es funktioniert hat ^^

    aber vllt fehlen auch einfach Infos...

    hier mal noch die Allocator-Klasse:

    template <class _Ty>
    class new_allocator
    {
    public:
    	_Ty* allocate(int iSize) { return (iSize == 1 ? new _Ty : new _Ty[iSize]); }
    	void release(_Ty* p, int iSize) { iSize == 1 ? delete p : delete[] p; }
    	template <class _Other>
    	struct rebind
    	{
    		typedef new_allocator<_Other> other;
    	};
    };
    

    jetzt wollte ich das eben in etwa so haben, wie ich das schon beim scoped_pointer gemacht hab,
    aber eben am auto_pointer, weil mir der doch etwas komplizierter erscheint.

    das mit dem Functionszeiger ist bei dir auch recht interessant, aber ich wollte das
    halt doch lieber mit templates machen.
    aber der Ansatz ist schonmal schön, vllt nehm ich ihn ja,
    wenns doch nicht anders geht.

    MfG DrakoXP



  • ok, ich hab das jetzt mal selber versucht, und das kam dabei raus:

    template <class _Ty, class _Allocator = new_allocator<_Ty>>
    class auto_pointer
    {
    private:
    	_Ty* m_pPtr;
    	_Allocator m_Allocator;
    public:
    	auto_pointer(void) : m_pPtr(0) {}
    	explicit auto_pointer(_Ty* p) : m_pPtr(p) {}
    	auto_pointer(auto_pointer<_Ty>& src) : m_pPtr(src.m_pPtr) { src.m_pPtr = 0; }
    	template <class _Other>
    	auto_pointer(auto_pointer<_Other, _Allocator::rebind<_Other>::other>& src) : m_pPtr((_Ty*)(src.m_pPtr)) { src.m_pPtr = 0; }
    	~auto_pointer(void) { if (m_pPtr) m_Allocator.release(m_pPtr, 1); }
    	_Ty* get(void) const { return m_pPtr; }
    	auto_pointer<_Ty>& operator = (const auto_pointer<_Ty>& src) { if (m_pPtr) m_Allocator.release(m_pPtr, 1); m_pPtr = src.m_pPtr; src.m_pPtr = 0; return *this; }
    	template <class _Other>
    	auto_pointer<_Ty>& operator = (const auto_pointer<_Other, _Allocator::rebind<_Other>::other>& src) { if (m_pPtr) m_Allocator.release(m_pPtr, 1); m_pPtr = (_Ty*)(src.m_pPtr); src.m_pPtr = 0; return *this; }
    	_Ty* operator -> (void) const { return m_pPtr; }
    	_Ty& operator * (void) const { return *m_pPtr; }
    };
    

    Abgesehen von der Sache mit den _X... wollte ich einfach mal fragen,
    ob das überhaupt so funktionieren könnte...



  • ok, also ich habe das jetzt mal getestet in einem Testprojekt,
    und da kommen jetzt Fehler, die ich nicht begreife xD

    warning C4346: '_Allocator::rebind<_Other>::other' : dependent name is not a type prefix with 'typename' to indicate a type
    error C2923: 'ctlib::auto_pointer' : '_Allocator::rebind<_Other>::other' is not a valid template type argument for parameter '_Allocator'

    kann mir das bitte jemand erklären?

    ich verzweifel hier 😞


  • Mod

    typename _Allocator::template rebind<_Other>::other
    

    Die Signaturen deiner Zuweisungsoperatoren sind allerdings sowieso falsch.


Anmelden zum Antworten