Move constructor and const member variables

前端 未结 5 954
自闭症患者
自闭症患者 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.

    0 讨论(0)
  • 2020-12-30 21:37

    Reference counting is standard approach that solves your problem. Consider adding reference counting to your class; either manually, or using existing tools like boost shared_ptr.

    0 讨论(0)
  • 2020-12-30 21:40

    This is why you should not declare said member variables const. const member variables usually serve no purpose. If you don't want users to mutate the FILE*, then don't provide them with functions to do that, and if you want to stop yourself from mutating it by accident, then mark your functions const. However, do not make member variables themselves const - because then you run into fun when you start to use move or copy semantics.

    0 讨论(0)
  • 2020-12-30 21:54

    The typical way to implement a move constructor is to zero out or otherwise invalidate the members of the instance being moved (see MSDN for a simple example). Therefore I would say just don't use const here as it is incompatible with the goals of move semantics.

    0 讨论(0)
  • 2020-12-30 21:56

    No, there is no way to do this. I would suggest that if you're really attached to the handle variable being const you should have a non-const flag member variable that indicates whether or not destruction should do anything.

    0 讨论(0)
提交回复
热议问题