Code VOR der Initialisierungsliste



  • Erstmal ne kurze Frage: Wo muß denn da was geschützt werden? Die Klasse existiert ja noch garnicht, wie kann dann jemand anderes sie im Moment benutzen?

    Ansonsten kannst Du natürlich als erstem Member der Klasse ein Guard-Objekt einbauen. Das initialisierst Du als erstes mit einem Lock, den gibst Du dann am Ende des Konstruktors frei. Aber wirklich schön ist das nicht. Sieht mir eher wie ein Design-Problem aus. Kannst Du die Synchronisation nicht nach vorne schieben? Aus dem Konstruktor in den umgebenden Code?



  • Die initialisierungsliste habe ich vergessen rauszunehmen, sorry.

    so meine ich es:

    Foo::Foo( const Foo& org)
    {
      GlobalCriticalSectionGuard guard;
      Swap (org); // Exception Sicher
    }
    
    void Foo::Swap (Foo & other) throw()
    {
        std::swap (something, other.something);
    }
    


  • SirLant schrieb:

    so meine ich es:

    Funktioniert nicht.



  • Shade Of Mine schrieb:

    SirLant schrieb:

    so meine ich es:

    Funktioniert nicht.

    Wieso nicht?



  • SirLant schrieb:

    Wieso nicht?

    kompilier es mal 😉

    Das Copy&Swap verwendet man bei op= - beim CopyCtor funktioniert es nicht. Da das kopieren ja mittels CopyCtor gemacht werden muss.

    Das Problem ist: wenn du kopierst, hast du das selbe Problem wie vorher: keinen Guard.

    und ein swap() ohne kopieren zerstört die ursprüngliche instanz.



  • Erstmal ne kurze Frage: Wo muß denn da was geschützt werden? Die Klasse existiert ja noch garnicht, wie kann dann jemand anderes sie im Moment benutzen?

    Die zu kopierende Klasse existiert.

    hm.Aber, bevor ichs falsch versteh:

    :something( Foo.something)

    entspricht

    :this.something( this.something)

    Geht das? this.something ist noch nicht initialisiert (und eine komplexe Klasse, die im Ctor was macht.

    <edit>
    Hatte vergessen die Initialisierungsliste rauszumachen (s.u.). Hilft aber auch nix, weil something keinen nicht-trivialen Ctor hat.
    </edit>

    Das initialisierst Du als erstes mit einem Lock, den gibst Du dann am Ende des Konstruktors frei. Aber wirklich schön ist das nicht.

    Hab ich mir schon gedacht. Man könnte das auch halbwegs schön machen. Aber wie ist das mit der exceptionsicherheit. Mir garantiert doch keiner, dass ich überhaupt ans Ende des Ctors, bzw. überhaupt in den Anweisungsblock des Ctors komme, da ja bereits in der Initialisierungsliste eine Exception fliegen kann.

    Sieht mir eher wie ein Design-Problem aus. Kannst Du die Synchronisation nicht nach vorne schieben? Aus dem Konstruktor in den umgebenden Code?

    Ich bin ja schon ganz oben. Meine Klasse ist eine umhüllende Klasse, die nur die Aufgabe hat, dafür zu sorgen, dass immer in die Critical Section gegangen wird.
    hm, das Problem könnte sein, dass die äußere Klasse direkt Members der untergeordneten Klassen hat. Dadurch sind die Lebenszeiten gleich lang. Vielleicht könnt ich die Bindung durch einen Pointer schwächen?

    //Jetzt
    
    class Umhuellend
    {
      Umhuellt umhuellt_;
    }
    
    //besser so
    
    class Umhuellend
    {
      smart_ptr<Umhuellt> pUmhuellt_;
    }
    
    //daraus ergibt sich dann
    
    Umhuellend::Umhuellend( const Umhuellend& org)
    //:umhuellt_(umhuellt)  das fällt raus
    {
      //critical Section
      pUmhuellt_.reset( new Umhuellt( *org.pumhuellend_));  //das ist neu
    }
    

    gut so? Andere Ideen?



  • Hast recht (wie so oft 🤡 ), wieder was dazu gelernt 🙂



  • Ist es irgendwie möglich, umhüllenden Code um die Initialisierungsliste eines Ctors zu machen.

    ned das ich wuesste, kommt mir aufn ersten Blick aber auch designmaessig etwas schraeg vor.

    Du willst doch die daten, die dem Konstruktor uebergibst, schuetzen, und ned die neu zu erstellende klasse oder ?

    das bedeutet das die Critical Section zu den Daten gehoert, und nicht in die neu zu erstellende Klasse ... deine CS sollte zumindest eine ahnlich lange lebensdauer haben ... eine CS in nem object selber nutzt dir da gar nix ....

    sprich dein Konstruktor sollte aufgerufen werden, wenn der Lock schon steht...

    Bau dir nen Object um deine Daten, und sicher den zugriff ab ... ruf den Konstruktor mit den abgesicherten methoden auf ...

    Beispiel:

    class MyData
    {
    public:
        /* ...*/
        unsigned int getTestData() const
        {
            mcs.Lock();
            unsigned int ireturn(testData);
            mcs.Unlock();
            return ireturn;
        }
    private:
        mutable CriticalSection mcs;
        unsigned int m_testData;
    };
    
    // irgendwo im code .... 
    // MyData data; irgendwo deklariert
       foo myfoo(data.getTestData());
    

    was anderes wird dir da nicht helfen ...

    Ciao ...



  • da ja bereits in der Initialisierungsliste eine Exception fliegen kann.

    was aber, glaub ich, kein problem ist .... wenn die klasse selber, deren Konstruktor / funktion die exception wirft, selber exceptionsicher und exceptionneutral ist .... es wird eh alles reuckgaengig gemacht und deine instanzierung der klasse schlaegt einfach fehl, und hinterlaesst kein spreicherleck ? oder ?

    Ciao ...



  • #include <iostream>
    using namespace std;
    
    class LibLock{
    	LibLock& operator=(LibLock const&);//forbidden
    public:
    	LibLock(){
    		lock();
    	}
    	LibLock(LibLock const&){
    		lock();
    	}
    	~LibLock(){
    		unlock();
    	}
    	static void lock(){
    		cout<<"lock"<<endl;
    	}
    	static void unlock(){
    			cout<<"unlock"<<endl;
    	}
    };
    
    class LibLockedBase{
    private:
    public:
    	LibLockedBase(){
    		LibLock::lock();
    	}
    	~LibLockedBase(){
    		LibLock::unlock();
    	}
    };
    
    template<class T>
    class LibLockingHandle:private LibLock{
    private:
    	T& data;
    public:
    	LibLockingHandle(T& t):
    	data(t){
    	}
    	T* operator->(){
    		return &data;
    	}
    };
    
    template<class T>
    class LibLockedWrapper{
    private:
    	T data;
    public:
    	typedef LibLockingHandle<T> Handle;
    	LibLockedWrapper(){
    		LibLock::unlock();
    	}
    	LibLockedWrapper(LibLockedWrapper const& l):
    	data(l.data){
    		LibLock::unlock();
    	}
    	~LibLockedWrapper(){
    		LibLock::lock();
    	}
    	Handle operator->(){
    		return Handle(data);
    	}
    	Handle handle(){
    		return Handle(data);
    	}
    };
    
    //volkard ist cool
    
    namespace impl{
    
    struct Member{
    public:
    	Member(){
    		cout<<"create member"<<endl;
    	}
    	Member(Member const&){
    		cout<<"copy member"<<endl;
    	}
    	~Member(){
    		cout<<"destruct member"<<endl;
    	}
    	void tuwas(){
    		cout<<"tuwas member"<<endl;
    	}
    };
    
    class Foo:public LibLockedBase{
    private:
    	Member m;
    public:
    	Foo(){
    	}
    	Foo(Foo const& f):
    	m(f.m){
    	}
    	~Foo(){
    	}
    	void tuwas(){
    		m.tuwas();
    	}
    };
    
    }
    
    typedef LibLockedWrapper<impl::Foo> Foo;
    
    int main(){
    	Foo a;								//lock create member unlock
    	cout<<"-------"<<endl;
    	Foo b(a);							//lock copy member unlock
    	cout<<"-------"<<endl;
    	b->tuwas();							//lock tuwas member unlock
    	cout<<"-------"<<endl;
    	{
    		Foo::Handle c=b.handle();	//lock
    		cout<<"-------"<<endl;
    		c->tuwas();						//tuwas member
    		c->tuwas();						//tuwas member
    		cout<<"-------"<<endl;
    	}										//unlock
    	cout<<"-------"<<endl;
    	{
    		Foo* c=&b;
    		cout<<"-------"<<endl;
    		(*c)->tuwas();					//lock tuwas member unlock
    		(*c)->tuwas();					//lock tuwas member unlock
    		cout<<"-------"<<endl;
    	}
    	cout<<"-------"<<endl;
    }//										lock destruct member unlock lock destruct member unlock
    


  • Basisklasse is ne gute Idee 🙂

    private wär aber schöner in dem Fall, oder?

    Vielen Dank Euch!



  • doppelposting



  • kartoffelsack schrieb:

    private wär aber schöner in dem Fall, oder?

    eigentlich dachte ich an

    void testIfDerivedFromLibLockedBase(LibLockedBase&){
    	}
    public:
    	typedef LibLockingHandle<T> Handle;
    	LibLockedWrapper(){
    		testIfDerivedFromLibLockedBase(data);
    		LibLock::unlock();
    	}
    

    aber es ist eh quatsch, wenn einer ein objekt der klasse LibLockedBase aleine anlegt.
    also

    class LibLockedBase{
    private:
    protected:
    	LibLockedBase(){
    		LibLock::lock();
    	}
    	~LibLockedBase(){
    		LibLock::unlock();
    	}
    public:
    };
    

    und dann darf man doch public erben, oder? also ich halte es für nicht schlecht, zuzugeben, woher geerbt wurde, denn diese erbschaft ist inhaltlich berechtigt.



  • Ist es irgendwie möglich, umhüllenden Code um die Initialisierungsliste eines Ctors zu machen

    Komma Operator in die Initialisierungsliste:

    class A{
      int b;
    public:
      A():
        b((foo1(),foo2(2),foo3(88/945*8+8-95),5)){
      }
    };
    

    Die doppelten Klammern sind nötig damit, dass der Compiler die Kommans nicht als Teil des Konstruktoraufufs von b betrachtet.

    PS: Komma Operator wehrte von links nach rechts aus und gibt den Wert des Recten Objects zurück. Also oben wird folgendes gemacht:

    foo1();
    foo2(2);
    foo3(88/945*8+8-95);
    int b=5;
    

    Ist zwar nicht schön allerdings ist es glaub ich genau das wonach du gefragt hast.



  • Und du darfst den Trinären Operator in der Initialisierungsliste benutzen, kannst
    also einiges da reinschreiben, auch wenn es nicht mehr so schön aussieht


Anmelden zum Antworten