开发者

Classes, constructor and pointer class members

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.


It depends on whether you want to share the underyling vector or not. In general, I think it is a good practice to avoid sharing wherever possible, since it removes the question of object ownership. Without sharing:

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

Note that, unlike in your example, I make it const since the original will not be modified. If, however, we want to hang on to and share the same underlying object, then we could use something like this:

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

Note that in this case, I choose to leave the caller as the owner of the object (so it is the caller's responsiblity to ensure that the list is around for the lifetime of the list handler object and that the list is later deallocated). Your example, in which you take over the ownership is also a possibility:

class ListHandler 
{
    public:
        ListHandler(std::vector<int>* list) : _list(list) {}
        ListHandler(const ListHandler& o) : _list(new std::vector<int>(o._list)) {}
        ~ListHandler(){ delete _list; _list=0; }

        ListHandler& swap(ListHandler& o){ std::swap(_list,o._list); return *this; }
        ListHandler& operator=(const ListHandler& o){ ListHandler cpy(o); return swap(cpy); }
    private:
        std::vector<int>* _list;
};

While the above is certainly possible, I personally don't like it... I find it confusing for an object that isn't simply a smart pointer class to acquire ownership of a pointer to another object. If I were doing that, I would make it more explicit by wrapping the std::vector in a smart pointer container as in:

class ListHandler 
{
    public:
        ListHandler(const boost::shared_ptr< std::vector<int> >& list) : _list(list) {}
        ~ListHandler(){}
    private:
        boost::shared_ptr< std::vector<int> > _list;
};

I find the above much clearer in communicating the ownership. However, all these different ways of passing along the list are acceptable... just make sure users know who will own what.


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.


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


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜