Current Implementation
I have a class containing unique_ptr
fields which depend on one other:
class ResourceManager {
ResourceManager() {}
ResourceManager(A* a_ptr) :
b_ptr(new B(a)),
c_ptr(new C(b_ptr.get())) {}
ResourceManager& operator=(ResourceManager&& that) {
// Call destructor, then construct a new instance on top
~ResourceManager();
ResourceManager* new_this = new(this) ResourceManager();
// Surely this must be the case, right?
// Is there any reason to prefer using either?
assert(new_this == this);
new_this->b_ptr = that.b_ptr;
new_this->c_ptr = that.c_ptr;
return *new_this;
}
unique_ptr<B> b;
unique_ptr<C> c;
};
Use case
The use case here is that I would like to reassign new values to the pointers, whilst keeping the ResourceManager
as a stack-allocated variable, or as a non-pointer class member.
With my current setup I imagine using it something like this:
A a, another_a;
ResourceManager r(&a);
// Use r...
// Destroy old ResourceManager and create the new one in place.
r = ResourceManager(&another_a);
The reason this is even a problem is due to the fact that B and C are non-assignable (for e.g. file streams)
Ugly Alternative
An alternative uglier (and dangerous) method would be to explicitly reset
the unique_ptr
fields crucially in reverse order (remember that C depends on B, and hence must be destructed first), effectively mimicking the default destruction behaviour.
ResourceManager& operator=(ResourceManager&& that) {
// Mimic destructor call (reverse-order destruction)
c_ptr.reset();
b_ptr.reset();
b_ptr = that.b_ptr;
c_ptr = that.c_ptr;
return *this;
}
Note that a wrong implementation would be to simply use the default assignment operator for ResourceManager
. This will assign the field in-order which implies in-order destruction of the unique_ptr
s, whereas we require reverse-order destruction.
Questions
Is this usage of this
pointer with placement new
and the explicit destructor call safe?
Must I use the returned new_this
pointer as opposed to the original this
pointer (for example, if the this
pointer technically becomes invalidated after calling the destructor)?
Are there any better suggested ways to achieve this? If add more such unique_ptr
fields to the class, I would have to make sure that I add a copy to the assignment operator. For instance, is it possible to call the move constructor instead, like so:
ResourceManager& operator=(ResourceManager&& that) {
// Call destructor
~ResourceManager();
// Move-construct a new instance on top
ResourceManager* new_this = new(this) ResourceManager(that);
return *new_this;
}
ResourceManager
by value and then swap this parameter with*this
. – ArdaC
depending on an instance ofB
that it does not own ? This is a recipe for failure and difficulties. If you could solve this root issue, then you would not have to worry about the defaulted assignment operator doing the wrong thing. – Plenipotentiarynew
in the assignment operator (in general) was already dealt with on SO. In general, it's a very bad idea. Most notably, your class should be markedfinal
else it's incorrect. As Herb Sutter put it so succinctly: Here Be Dragons. – PlenipotentiaryB
andC
form various "chains" of filestream wrappers (e.g.ZipInputStream
, etc.). They indeed ought to be taking ownership of the pointers. – Orderly