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 during the whole object life time and the destructor finally closes it. (That is the idea behind RAII, right?)
But with the C++0x move constructor i run into a problem. Since the destructor is also called on the "unloaded" object i need to prevent the cleanup of the resource handle. Since the member variable is const i have no way to assign the value -1 or INVALID_HANDLE (or equivalent values) to indicate to the destructor that it should not do anything.
Is there a way that the destructor is not called if the state of an object was moved to another object?
Example:
class File
{
public:
// Kind of "named constructor" or "static factory method"
static File open(const char *fileName, const char *modes)
{
FILE *handle = fopen(fileName, modes);
return File(handle);
}
private:
FILE * const handle;
public:
File(FILE *handle) : handle(handle)
{
}
~File()
{
fclose(handle);
}
File(File &&other) : handle(other.handle)
{
// The compiler should not call the destructor of the "other"
// object.
}
File(const File &other) = delete;
File &operator =(const File &other) = delete;
};
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.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 theFILE*
, 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 functionsconst
. However, do not make member variables themselvesconst
- 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.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.
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:
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.,
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
: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.