Reputation: 2531
What is a good way of passing an object as a parameter? In my code, I have been using references instead of pointers, and I would like to stick to this approach if possible. However, there is one case where this does not work properly:
class Resource {
Resource(...) { //expensive resource initialization }
~Resource() { //destroy resource }
};
class User {
User(const Resource & res) : res_(res) { //initialize user }
private:
Resource res_;
}
void Init() {
Resource res(...);
User user = new User(res);
} //res is destroyed - along with its associated resource
//user can no longer use the resource
Are there any best practices that should be used in such a scenario? The result I want to achieve is to destroy the Resource
automatically when the User
object is destroyed, but not rely on the User
object to create the Resource
itself. My feeling is that I should overload the copy constructor of Resource
. Any other ideas?
Upvotes: 0
Views: 166
Reputation: 4863
Your best shot is holding a shared_ptr<Resource>
member in User
class. But this requires the resource to be generated on the free (heap) memory.
class User {
User(Resource * res) : res_ptr(res) { //initialize user }
private:
shared_ptr<Resource> res_ptr;
}
void Init() {
Resource * res = new Resource(...);
User user = new User(res);
}
Second approach is providing a fake copy constructor and a swap mechanism for Resource
.
class Resource
{
private:
explicit Resource(const Resource &); // disabled to avoid heavy copy.
const Resource & operator = (const Resource & );
int * int_res;
public:
Resource() : int_res(new int(100)) { }
~Resource()
{
if(int_res != NULL)
delete int_res;
}
Resource(Resource & other) : int_res(NULL)
{
this->swap(other);
}
void swap(Resource & other)
{
using std::swap;
swap(int_res, other.int_res);
}
};
class User
{
private:
Resource resource;
User();
User(const User &);
const User & operator = (const User &);
public:
User(Resource & res) : resource(res) { }
~User() { }
};
But this is dangerous and error-prone because after creating the user, you leave the resource in a zombie state. For this piece of code, all access to the int_res
pointer, which is assgined to NULL
, will give you access violation error.
The last approach I can explain is using a C++0x feature "move semantics".
class Resource
{
// ...
// ...
// Replace with Resource(Resource & other)
Resource(Resource && other) : int_res(NULL) //Move constructor
{
this->swap(other);
}
const Resource & operator = (Resource && other) // Move assignment operator
{
this->swap(other);
return *this;
}
void swap(Resource & other)
{
using std::swap;
swap(int_res, other.int_res);
}
};
class User
{
private:
Resource resource;
User();
User(const User &);
const User & operator = (const User &);
public:
User(Resource && res) : resource(std::move(res)) { }
~User() { }
};
void Init() {
Resource res(...);
User user = new User(std::move(res));
}
This approach is some how safer. You don't have to deal with pointers and if you forget to write std::move
, you will get a compiler error saying "you cannot bind a Resource
instance to Resource&&
" or "copy construtor of Resource
is disabled."
&&
emphasizes the object is temporary and is going to evaporate. So this approach is more suitable for scenarios where Resource is generated and returned by a function. If I were you I would make the constructor private and generate it with a friend function.
Resource GetResource(....)
{
Resource res(...);
return res;
}
void Init()
{
User user = new User( GetResource(...) );
}
This piece of code is perfectly performant with "move semantics". You must learn it if you are able to write C++0x code. This and this are two good videos to start with.
Upvotes: 1