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
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.
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.
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.
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.
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.