shared_from_this called from constructor

前端 未结 7 1965
一生所求
一生所求 2021-02-03 21:31

I have to register an object in a container upon its creation. Without smart pointers I\'d use something like this:

a_class::a_class()
{
    register_somewhere(t         


        
相关标签:
7条回答
  • 2021-02-03 22:11

    For this purpose, I wrote my own drop-in replacement for shared_ptr, weak_ptr and enable_shared_from_this. You can check it out at Sourceforge.

    It allows call of shared_from_this() in constructor and destructor without helper functions without space overhead compared to the standard enable_shared_from_this.

    Note: creating shared_ptr's in dtors is only allowed as long as they are created only temporarily. That is, they are being destroyed before the dtor returns.

    0 讨论(0)
  • 2021-02-03 22:16

    I came up with a helper class for that problem:

    template <class Impl>
    class ImmediatelySharedFromThis : public std::enable_shared_from_this<Impl> {
        typedef std::unique_ptr<void, std::function<void(void*)>> MallocGuard;
        typedef std::shared_ptr<Impl> SharedPtr;
    
        // disallow `new MyClass(...)`
        static void *operator new(size_t) = delete;
        static void *operator new[](size_t) = delete;
        static void operator delete[](void*) = delete;
    protected:
        typedef std::pair<MallocGuard&, SharedPtr&> SharingCookie;
    
        ImmediatelySharedFromThis(SharingCookie cookie) {
            MallocGuard &ptr = cookie.first;
            SharedPtr &shared = cookie.second;
            // This single line contains the actual logic:
            shared.reset(reinterpret_cast<Impl*>(ptr.release()));
        }
    public:
        // Create new instance and return a shared pointer to it.
        template <class ...Args>
        static SharedPtr create(Args &&...args) {
            // Make sure that the memory is free'd if ImmediatelySharedFromThis
            // is not the first base class, and the initialization
            // of another base class throws an exception.
            MallocGuard ptr(aligned_alloc(alignof(Impl), sizeof(Impl)), free);
            if (!ptr) {
                throw std::runtime_error("OOM");
            }
    
            SharedPtr result;
            ::new (ptr.get()) Impl(SharingCookie(ptr, result),
                                   std::forward<Args>(args)...);
            return result;
        }
    
        static void operator delete(void *ptr) {
            free(ptr);
        }
    };
    
    class MyClass : public ImmediatelySharedFromThis<MyClass> {
        friend class ImmediatelySharedFromThis<MyClass>;
    
        MyClass(SharingCookie cookie, int some, int arguments) :
            ImmediatelySharedFromThis(cookie)
            // You can pass shared_from_this() to other base classes
        {
            // and you can use shared_from_this() in here, too.
        }
    public:
        ....
    };
    
    ...
    
    std::shared_ptr<MyClass> obj = MyClass::create(47, 11); 
    

    A bit ugly, but it works.

    0 讨论(0)
  • 2021-02-03 22:19

    There is no need for shared_ptr in your code (as you show it and explain it). A shared_ptr is only appropriate for shared ownership.

    Your b_class doesn't own it's a_class, in fact is even outlived by it, so it should merely keep an observing pointer.

    If b_class is polymorphic and the manipulations of a_class involve altering its b_class pointer, you should use a unique_ptr<b_class>:

    class a_class;
    class b_class
    {
      friend class a_class;
      a_class* mya;
      b_class(a_class*p)
      : mya(p) {}
    public:
      virtual~b_class() {}   // required for unique_ptr<b_class> to work
      virtual void fiddle(); // do something to mya
    };
    
    class a_class
    {
      std::unique_ptr<b_class> myb;
    public:
      a_class()
      : myb(new b_class(this)) {}
      template<typename B>
      void change_myb()
      {
        myb.reset(new B(this));
      }
    };
    
    0 讨论(0)
  • 2021-02-03 22:20

    Why don't you use http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/enable_shared_from_this.html

    struct a_class : enable_shared_from_this<a_class> {
        a_class() {
            shared_ptr<a_class> ptr(this);
            register_somewhere(ptr);
        }
    };
    

    Update: here is a complete working example:

    #include <stdio.h>
    #include <boost/smart_ptr/enable_shared_from_this.hpp>
    
    struct a_class;
    boost::shared_ptr<a_class> pa;
    
    void register_somewhere(boost::shared_ptr<a_class> p)
    {
        pa = p;
    };
    
    struct a_class : boost::enable_shared_from_this<a_class> {
    private:
        a_class() {
            printf("%s\n", __PRETTY_FUNCTION__);
            boost::shared_ptr<a_class> ptr(this);
            register_somewhere(ptr);
        }
    
    public:
        ~a_class() {
            printf("%s\n", __PRETTY_FUNCTION__);
        }
    
        static boost::shared_ptr<a_class> create()
        {
            return (new a_class)->shared_from_this();
        }
    };
    
    int main()
    {
        boost::shared_ptr<a_class> p(a_class::create());
    }
    

    Note the factory function a_class::create(). Its job is to make sure that only one reference counter gets created. Because

    boost::shared_ptr<a_class> p(new a_class);
    

    Results in creation of two reference counters and double deletion of the object.

    0 讨论(0)
  • 2021-02-03 22:24

    If you absolutely need a shared_ptr during construction, it's best to have an 'init' function. In fact, this is the only decent approach I can think of. You should probably have a special function that creates objects of this type, to ensure init() is called, if you choose this path.

    However, depending on what you're registering for, it may be a better idea to give whatever object you're registering with a plain pointer to the object in the constructor, rather than a shared_ptr. Then in the destructor, you can just unregister the object from the manager.

    0 讨论(0)
  • 2021-02-03 22:28

    a_class is responsible for creating and destroying b_class instances

    ...

    a_class instance "survives" b_class instances.

    Given these two facts, there should be no danger that a b_class instance can attempt to access an a_class instance after the a_class instance has been destroyed as the a_class instance is responsible for destroying the b_class instances.

    b_class can just hold a pointer to it's associated a_class instance. A raw pointer doesn't express any ownership which is appropriate for this case.

    In this example it doesn't matter how the a_class is created, dynamically, part of a aggregated object, etc. Whatever creates a_class manages its lifetime just as a_class manages the lifetime of the b_class which it instantiates.

    E.g.

    class a_class;
    
    class b_class
    {
    public:
        b_class( a_class* a_ ) : a( a_ ) {}
    private:
        a_class* a;
    };
    
    class a_class
    {
    public:
        a_class() : b( new b_class(this) ) {}
    private:
        boost::shared_ptr<b_class> b;
    };
    

    Note, in this toy example there is no need for a shared_ptr, an object member would work just as well (assuming that you don't copy your entity class).

    class a_class
    {
    public:
        a_class() : b( this ) {}
    private:
        b_class b;
    };
    
    0 讨论(0)
提交回复
热议问题