Move constructor and const member variables

前端 未结 5 958
自闭症患者
自闭症患者 2020-12-30 21:24

I like the idea of const member variables especially when I wrap C functions into classes. The constructor takes a resource handle (e.g. a file descriptor) that stays valid

5条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-30 21:30

    Actually, I've run into this problem myself as well today. Not willing to accept 'can't be done' & 'use shared_ptr / reference counting', googling more, I came up with this base class:

    class Resource
    {
    private:
         mutable bool m_mine;
    
    protected:
        Resource()
        : m_mine( true )
        {
        }
    
        Resource(const Resource&)       = delete;
        void operator=(const Resource&) = delete;
    
        Resource(const Resource&& other)
        : m_mine( other.m_mine )
        {
            other.m_mine = false;
        }
    
        bool isMine() const
        {
            return m_mine;
        }
    };
    

    All methods and constructors are protected, you need to inherit from it to use it. Notice the mutable field: this means that descendant can be a const member in a class. E.g.,

    class A : protected Resource
    {
    private:
        const int m_i;
    
    public:
        A()
        : m_i( 0 )
        {
        }
    
        A( const int i )
        : m_i( i )
        {
        }
    
        A(const A&& a)
        : Resource( std::move( a     ) )
        , m_i     ( std::move( a.m_i ) ) // this is a move iff member has const move constructor, copy otherwise
        {
        }
    
        ~A()
        {
            if ( isMine() )
            {
                // Free up resources. Executed only for non-moved objects
                cout << "A destructed" << endl;
            }
        }
    };
    

    Field(s) of A can be const now. Note that I've inherited protected, so that user cannot accidentally cast A to Resource (or very willingly to hack it), but A is still not final, so you can still inherit from this (a valid reason to inherit from a Resource is e.g. to have separate read and read-write access). This is one of the extremely rare cases when protected inheritance doesn't automatically mean that your design is faulty; however, if you find it difficult to understand, you might just use public inheritance.

    Then, assuming you have a struct X:

    struct B
    {
        const A m_a;
        const X m_x;
    
        B(const A&& a, const X& x) // implement this way only if X has copy constructor; otherwise do for 'x' like we do for 'a'
        : m_a( std::move( a ) )
        , m_x(            x   )
        {
        }
    
        B( const B&& b )
        : m_a( std::move( b.m_a ) )
        , m_x( std::move( b.m_x ) ) // this is a move iff X has move constructor, copy otherwise
        {
        }
    
        ~B()
        {
            cout << "B destructed" << endl;
        }
    };
    

    Note that fields of B can also be const. Our move constructors are const. Given your types have appropriate move constructors, any heap-allocated memory can be shared amongst objects.

提交回复
热议问题