Objekt NICHT im Ctor initialisieren



  • herausfordern? Ich wollte nur wissen ob man irgendwie ein Objekt anders initialisieren kann. So grausigen Code bau ich bestimmt nicht bei mir ein^^



  • Der Code ist nicht krausig, sondern von dir NICHT verstanden und so von dir als komplex eingestuft:)

    Deine Variante ist auch nicht grad elegant. Du müsstest es dann so machen, was für mich krausig wäre:)

    void Foo::init(Bar *p){
     b2=b;
    };
    
    Foo o;
    o.init(new Bar(...));
    

    Erzeug doch das Objekt gleich am anfang, und setzte die member variablen später..


  • Mod

    hab noch ein klein wenig gebastelt. einfach delayed_init<T> an Stelle von T benutzen und die Initialisierung mittels construct durchführen. sollte einigermaßen autoplausibel sein:

    #include <cassert>
    #include <algorithm>
    #include <boost/type_traits/aligned_storage.hpp>
    #include <boost/type_traits/alignment_of.hpp>
    
    template <typename T>
    class delayed_init
    {
    public:
        typedef T value_type;
    
        delayed_init()
            : initialized_( false )
        {}
        delayed_init(const delayed_init& other)
            : initialized_( other.is_initialized() )
        {
            if ( is_initialized() )
                new( &data_ ) value_type( other.data() );
        }
        delayed_init(const value_type& other)
            : initialized_( true )
        {
            new( &data_ ) value_type( other );
        }
        delayed_init& operator=(const delayed_init& rhs)
        {
            if ( is_initialized() != rhs.is_initialized() )
            {
                if ( is_initialized() )
                    destroy();
                else
                    construct( rhs.data() );
            }
            else if ( is_initialized() )
                data() = rhs.data();
            return *this;
        }
        ~delayed_init()
        {
            if ( is_initialized() )
                data().~value_type();
        }
    /*
        delayed_init(delayed_init&& other)
            : initialized_( other.is_initialized() )
        {
            if ( is_initialized() )
                new( &data_ ) value_type( std::move( other.data() ) );
        }
        delayed_init(value_type&& other)
            : initialized_( true )
        {
            new( &data_ ) value_type( other );
        }
        delayed_init& operator=(delayed_init&& rhs)
        {
            if ( is_initialized() != rhs.is_initialized() )
            {
                if ( is_initialized() )
                    destroy();
                else
                    construct( std::move( rhs.data() ) );
            }
            else if ( is_initialized() )
                data() = std::move( rhs.data() );
            return *this;
        }
    */
    
        bool is_initialized() const { return initialized_; }
    
        void destroy()
        {
            assert( is_initialized() );
            data().~value_type();
            initialized_ = false;
        }
    
        void construct()
        {
            assert( !is_initialized() );
            new( &data ) value_type();
            initialized_ = true;
        }
        template <typename T1> void construct(const T1& arg1)
        {
            assert( !is_initialized() );
            new( &data ) value_type( arg1 );
            initialized_ = true;
        }
        template <typename T1, typename T2> void construct(const T1& arg1, const T2& arg2)
        {
            assert( !is_initialized() );
            new( &data ) value_type( arg1, arg2 );
            initialized_ = true;
        }
        template <typename T1, typename T2, typename T3> void construct(const T1& arg1, const T2& arg2, const T3& arg3)
        {
            assert( !is_initialized() );
            new( &data ) value_type( arg1, arg2, arg3 );
            initialized_ = true;
        }
    // usw. oder
    /*
        template <typename... Args>
        void construct(Args&& args)
        {
            assert( !is_initialized() );
            new( &data ) value_type( std::forward< Args... >( args... ) );
            initialized_ = true;
        }
    */
        void swap(delayed_init& other)
        {
            using std::swap;
            if ( is_initialized() != other.is_initialized() )
            {
                delayed_init& old_data = is_initialized() ? *this : other;
                delayed_init& new_data = !is_initialized() ? *this : other;
                new_data.construct( old_data.data() );
                old_data.destroy();
            }
            else
            {
                if ( is_initialized() )
                    swap( data(), other.data() );
            }
        }
        friend void swap(delayed_init& lhs, delayed_init& rhs)
        {
            lhs.swap( rhs );
        }
    
        value_type& data() { return reinterpret_cast< value_type& >( data_ ); }
        const value_type& data() const { return reinterpret_cast< const value_type& >( data_ ); }
    
        operator value_type&() { return data(); }
        operator const value_type&() const { return data(); }
    private:
        bool initialized_;
        typename boost::aligned_storage< sizeof( value_type ), boost::alignment_of< value_type >::value >::type data_;
    };
    


  • BorisDieKlinge schrieb:

    ...
    Deine Variante ist auch nicht grad elegant. Du müsstest es dann so machen, was für mich krausig wäre:)...

    BTW: Da einen im HorrorFilm auch nicht "das Krauen" packt, ist auch dieser Code niemals "krausig" 😉

    Naja, das kann man durchaus einfacher und Konsistenter machen:

    class Foo {
       Bar* b;
    public:
       Foo() : b(0) {}
       void initB(int bCtorArg1, string bCtorArg2){
          if(b) throw runtime_exception("duplicate init");
          // ... kann auch das alte löschen .. wie man's eben braucht
          b=new Bar(bCtorArg1, bCtorArg2);
       }
       ~Foo() { delete b; }
    };
    

    Für mich ist eher die Frage, was das denn für ein Member sein soll, das nicht gleichzeitig mit seinem "Aggregierenden" erzeugt werden soll.
    Falls es sich um eine(n) "Verknüpfung/Verweis" handelt (was ich vermute), ist der Pointer sowieso das Mittel der Wahl für optionale/veränderbare Verweise in C++ - unabhängig vom konkreten Problem.
    (auch, wenn ich mir wünschen würde, dass man die "Nicht-Ownership" kennzeichnen könnte 😉 )

    Gruß,

    Simon2.



  • Simon2 schrieb:

    ...
    Naja, das kann man durchaus einfacher und Konsistenter machen:...

    Ich sehe da nur wieder das Problem das man die Klasse einfach und konsequent falsch verwenden kann. Eine Init-Methode kann man sehr leicht im Code vergessen. Konsequent wäre es nur wenn man es wirklich bei allen Klassen so macht.

    cu André



  • asc schrieb:

    ...
    Ich sehe da nur wieder das Problem das man die Klasse einfach und konsequent falsch verwenden kann. Eine Init-Methode kann man sehr leicht im Code vergessen. ...

    DAS liegt aber nicht an meiner Lösung, sondern an der "Aufgabenstellung" !
    Der CTor ist halt die einzige zuverlässige Stelle für Initialisierungen ... und wenn er es da nicht haben will, sondern "irgendwann später", dann gibt er die Kontrolle aus der Hand - mit den verbundenen Risiken.
    Aus diesem Grund würde ich auch so eine "optionale Initialisierung" möglichst nicht machen...

    Andererseits: Wenn es sich um eine "Option" handelt, ist klar, dass diese Klasse "funktionieren" muss, auch ohne dass dieses Member initialisiert wurde.

    Wie gesagt: Es ist mir noch nicht ganz klar, was es fachlich bedeuten soll, ein Member "später" zu initialisieren ... das riecht für mich nach einem von

    • "Verweis" (dann sollte aber die Klasse das Ding auch nicht initialisieren)
    • schlechte oder missbrauchte FremdAPI oder
    • schlechtes Design/Unwissenheit.

    Gruß,

    Simon2.



  • ich hasse boost 👎



  • Simmy2 schrieb:

    ich hasse boost 👎

    Niemand zwingt dich boost einzusetzen, ebenso wie die STL (Und da schließlich einiges von Boost nicht ohne Grund in den nächsten Standard übernommen wird, müsstest du auch die STL hassen).

    cu André



  • @camper: ich nehme für sowas gleich boost::optional 😉



  • Auch wenn hier nach Hilfe bei der Implementierung gefragt ist scheints evtl. soch ein Designproblem zu sein das mit dem falschen Ansatz geloest wird. Beschreib uns doch mal was du erreichen willst, dann gibts vielleicht ne Alternative zu der verspaeteten Initialisierung.



  • camper schrieb:

    hab noch ein klein wenig gebastelt. einfach delayed_init<T> an Stelle von T benutzen und die Initialisierung mittels construct durchführen. sollte einigermaßen autoplausibel sein:

    #include <cassert>
    #include <algorithm>
    #include <boost/type_traits/aligned_storage.hpp>
    #include <boost/type_traits/alignment_of.hpp>
    
    template <typename T>
    class delayed_init
    {
    public:
        typedef T value_type;
    
        delayed_init()
            : initialized_( false )
        {}
        delayed_init(const delayed_init& other)
            : initialized_( other.is_initialized() )
        {
            if ( is_initialized() )
                new( &data_ ) value_type( other.data() );
        }
        delayed_init(const value_type& other)
            : initialized_( true )
        {
            new( &data_ ) value_type( other );
        }
        delayed_init& operator=(const delayed_init& rhs)
        {
            if ( is_initialized() != rhs.is_initialized() )
            {
                if ( is_initialized() )
                    destroy();
                else
                    construct( rhs.data() );
            }
            else if ( is_initialized() )
                data() = rhs.data();
            return *this;
        }
        ~delayed_init()
        {
            if ( is_initialized() )
                data().~value_type();
        }
    /*
        delayed_init(delayed_init&& other)
            : initialized_( other.is_initialized() )
        {
            if ( is_initialized() )
                new( &data_ ) value_type( std::move( other.data() ) );
        }
        delayed_init(value_type&& other)
            : initialized_( true )
        {
            new( &data_ ) value_type( other );
        }
        delayed_init& operator=(delayed_init&& rhs)
        {
            if ( is_initialized() != rhs.is_initialized() )
            {
                if ( is_initialized() )
                    destroy();
                else
                    construct( std::move( rhs.data() ) );
            }
            else if ( is_initialized() )
                data() = std::move( rhs.data() );
            return *this;
        }
    */
    
        bool is_initialized() const { return initialized_; }
    
        void destroy()
        {
            assert( is_initialized() );
            data().~value_type();
            initialized_ = false;
        }
    
        void construct()
        {
            assert( !is_initialized() );
            new( &data ) value_type();
            initialized_ = true;
        }
        template <typename T1> void construct(const T1& arg1)
        {
            assert( !is_initialized() );
            new( &data ) value_type( arg1 );
            initialized_ = true;
        }
        template <typename T1, typename T2> void construct(const T1& arg1, const T2& arg2)
        {
            assert( !is_initialized() );
            new( &data ) value_type( arg1, arg2 );
            initialized_ = true;
        }
        template <typename T1, typename T2, typename T3> void construct(const T1& arg1, const T2& arg2, const T3& arg3)
        {
            assert( !is_initialized() );
            new( &data ) value_type( arg1, arg2, arg3 );
            initialized_ = true;
        }
    // usw. oder
    /*
        template <typename... Args>
        void construct(Args&& args)
        {
            assert( !is_initialized() );
            new( &data ) value_type( std::forward< Args... >( args... ) );
            initialized_ = true;
        }
    */
        void swap(delayed_init& other)
        {
            using std::swap;
            if ( is_initialized() != other.is_initialized() )
            {
                delayed_init& old_data = is_initialized() ? *this : other;
                delayed_init& new_data = !is_initialized() ? *this : other;
                new_data.construct( old_data.data() );
                old_data.destroy();
            }
            else
            {
                if ( is_initialized() )
                    swap( data(), other.data() );
            }
        }
        friend void swap(delayed_init& lhs, delayed_init& rhs)
        {
            lhs.swap( rhs );
        }
    
        value_type& data() { return reinterpret_cast< value_type& >( data_ ); }
        const value_type& data() const { return reinterpret_cast< const value_type& >( data_ ); }
    
        operator value_type&() { return data(); }
        operator const value_type&() const { return data(); }
    private:
        bool initialized_;
        typename boost::aligned_storage< sizeof( value_type ), boost::alignment_of< value_type >::value >::type data_;
    };
    

    Sorry, aber einfach nur lol. Ich soll um EINMAL ein billiges Objekt zu initialisieren boost installieren und so ellenlangen megagrausigen Code benutzen? Oh, aber Moment. Der Code ist natürlich kein Overkill und eklig - ich versteh ihn natürlich nur nicht. 🙄
    Ich bleib beim Pointer. Ihr könnt den Thread schließen. 🙂



  • Willkommen im Leben mein Freund.

    Wenn du eine gute Lösung willst, dann musst du auch etwas dafür tun. Das delayed_init sieht schön aus. boost sollte sowieso jeder installiert haben und notfalls geht der code auch ohne boost.

    worum es aber geht: es ist eine schöne Lösung die man immer wieder verwenden kann.


  • Mod

    hustbaer schrieb:

    @camper: ich nehme für sowas gleich boost::optional 😉

    ach das wars. Ich hatt an variant und in_place_factory gedacht, aber beide sind nicht ganz das, was ich machen wollte. boost hat einfach zuviele Komponenten 🙂



  • Mach aus "zuviele" ein "viele", und du hast Recht. 😉



  • CentaurWarchief schrieb:

    ...
    Ich bleib beim Pointer. Ihr könnt den Thread schließen. 🙂

    Hast Du irgendetwas von dem Rest hier gelesen ?

    Wenn nicht: Dann bis bald,

    Simon2.



  • Ist doch völlig unerheblich. Ich kann den Code gut gebrauchen (hatte nämlich ein Problem, das gerade diese verzögerte Initialisierung brauchte) 😉


Anmelden zum Antworten