views:

128

answers:

5

I'm a bit confused about the object references. Please check the examples below:

class ListHandler {
public:
   ListHandler(vector<int> &list);
private:
   vector<int> list;
}

ListHandler::ListHandler(vector<int> &list) {
   this->list = list;
}

Because of the internal

vector<int> list;

definition, here I would be wasting memory right? So the right one would be:

class ListHandler {
public:
   ListHandler(vector<int>* list);
private:
   vector<int>* list;
}

ListHandler::ListHandler(vector<int>* list) {
   this->list = list;
}

ListHandler::~ListHandler() {
   delete list;
}

Basically all I want is to create a vector and pass to ListHandler. This vector will not be used anywhere else than the ListHandler itself so I'm expecting ListHandler to do all the other things and cleanup etc. stuff.

A: 

There's no single "right way." Your second example would be very poor style, however, because the ListHandler acquires ownership of the vector when it is constructed. Every new should be closely paired with its delete if at all possible — seriously, that is a very high priority.

If the vector lives as long as the ListHandler, it might as well live inside the ListHandler. It doesn't take up any less space if you put it on the heap. Indeed, the heap adds some overhead. So this is not a job for new at all.

You might also consider

ListHandler::ListHandler(vector<int> &list) {
   this->list.swap( list );
}

if you want the initializer list to be cleared and avoid the time and memory overhead of copying the vector's contents.

Potatoswatter
Swap swaps the contents, which is not what I want to do. I just don't want to create 2 vector objects. I guess in my definition:vector<int> list;creates one into the class. In constructor, I'm passing one another vector to it, but it is already created. So I'm passing reference, but this is something like I'm creating a vector and then my variable starts to point out to another vector which is not good.
pocoa
I wouldn't consider the second example poor style at all. It's not such a "very high priority" to pair new/delete closely. Factory functions separate them completely. The second example is a fine example of transferral of ownership.
Stephen
@pocoa: swap simply moves the contents of the source list to the destination. When you pass a source vector, it might be on the stack. The vector inside the `ListHandler` must be somewhere else. Therefore, you must have two vector objects. They are very lightweight; you only make your program bigger and slower by attempting to minimize them with `new vector`.
Potatoswatter
@Stephen: No, a factory function simply takes the place of `new`. The call to the factory gets paired with `delete` just the same as `new` would. Excessive transferral of ownership defeats RAII which is the memory management philosophy of C++.
Potatoswatter
@Potatocorn: I think it's better to use "assign", instead of "swap".
pocoa
@pocoa: If you don't want to have two copies of the data, you must use swap. The next version of C++ will introduce a new function `move` which is more elegantly named, but in the current C++03, swapping with an empty vector is the only way to move data from one to the other in O(1) time.
Potatoswatter
+1  A: 

The first example isn't necessarily wasting memory, its just making a copy of the entire vector at the "this->list = list;" line (which could be what you want, depends on the context). That is because the operator= method on the vector is called at that point which for vector makes a full copy of itself and all its contents.

The second example definitely isn't making a copy of the vector, merely assigning a memory address. Though the caller of the ListHandler contructor better realize that ListHandler is taking over control of the pointer, since it will end up deallocating the memory in the end.

cpalmer
So, you you mean that second one is right?
pocoa
If the vector<int> is created via "new" and then ownership of that vector is to be transferred to the ListHandler who will destroy it later, and you don't want make a copy of the vector in the process, then the second option will do that for you.
cpalmer
But you should have a reason to transfer ownership besides trying to save memory by having "fewer" `vector` objects.
Potatoswatter
+1  A: 

It depends on whether the caller expects to keep using their list (in which case you better not delete it, and need to worry about it changing when you least expect), and whether the caller is going to destroy it (in which case you better not keep a pointer to it).

If the documentation of your class is that the caller allocates a list with new and then turns ownership over to your class when calling your constructor, then keeping the pointer is fine (but use auto_ptr so you don't have to write "delete list" yourself and worry about exception safety).

Ben Voigt
Yes, all I want to do is to transfer the ownership to the ListHandler. Is the second one good for this purpose?
pocoa
Yes, your second example is fine.
Stephen
+1  A: 
Michael Aaron Safyan
Watch out: in the 3rd example (object owner) you did not redefine the Copy and Assignment, thus you have effectively Undefined Behavior waiting to happen. Also, it's not a good idea to take ownership without being explicit about it, if you take ownership of the pointer, require to be passed a smart pointer like `unique_ptr` in your interface so that the caller cannot accidentally call it with the reference to an object on the stack, for example.
Matthieu M.
@Mathhieu, thanks. Good catch. I'll fix that. One of the reasons I don't like that solution, myself.
Michael Aaron Safyan
+1  A: 

It all depends what you want, and what policies you can ensure. There is nothing "wrong" with your first example (though I would avoid explicitly using this-> by choosing different names). It makes a copy of the vector, and that may be the right thing to do. It may be the safest thing to do.

But it looks like you would like to reuse the same vector. If the list is guaranteed to outlive any ListHandler, you can use a reference instead of a pointer. The trick is that the reference member variable must be initialized in an initialization list in the constructor, like so:

class ListHandler
{
public:
    ListHandler(const vector<int> &list)
    : list_m(list)
    {
    }
private:
    vector<int>& list_m;
};

The initialization list is the bit after the colon, but before the body.

However, this is not equivalent to your second example, which using pointer and calls delete in its destructor. That is a third way, in which the ListHandler assumes ownership of the list. But the code comes with dangers, because by calling delete it assumes the list was allocated with new. One way to clarify this policy is by using a naming convention (such as an "adopt" prefix) that identifies the change of ownership:

ListHandler::ListHandler(vector<int> *adoptList)
: list_m(adoptList)
{
}

(This is the same as yours, except for the name change, and the use of an initialization list.)

So now we have seen three choices:

  1. Copy the list.
  2. Keep a reference to a list that someone else owns.
  3. Assume ownership of a list that someone created with new.

There are still more choices, such as smart pointers that do reference counting.

Jon Reid