1

I have a vector with raw pointers (no, I cannot use smart pointers) and I want to add items to the list in a for loop. I've made a little trial project, and I wondered if this is considered good C++ code in terms of pointer management.

Please only consider raw pointer management, I am not interested in smart pointers for this particular problem I'm trying to solve.

A simple object:

class Request
{
public:
    std::string name;

};

std::vector<Request*> requests;

for (int i = 0; i < 5; i++)
{
    std::stringstream ss;
    ss << "elemenent ";
    ss << i;

    std::string s = ss.str();

    Request* req = new Request();   
    req->name = s;

    requests.push_back(req);

}

EDIT:

So the problem I am trying to solve is adding the DOMNode* to a vector from this library.
I'm starting to get the feeling that trying to write a wrapper for the parts I need from this library for my project, is a bad idea. Or maybe the library is no good? I haven't got it to work properly using smart_ptr, if anybody out there has, then I'd like to hear about it.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • If you're not using smart pointers then it all depends on how you intend to clean up those Requests. If this was in our review I'd also suggest using a constructor to initialise the request name. – Adam Bowen Nov 09 '10 at 09:53
  • 2
    It is really unclear what you are asking for. There is no pointer management in this code. You are creating some objects on the heap and then never delete them. – Björn Pollex Nov 09 '10 at 09:54
  • It's simple really, I am using a library that only uses pointers and no objects have copy constructors. Some of these objects I need to add to a list, I want to know what the correct way is. – Tony The Lion Nov 09 '10 at 09:59

5 Answers5

5

Well, this leaks memory, so it is bad. Can you use a Pointer Container?

The reason this code leaks is because you create objects on the heap using new, but you never call delete on them.

As for you comment, if you have an object that manually manages some resource, you need The Big Three.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • Great, I suspected I was leaking... What if the Request had only a private copy ctor and contained pointer members? – Tony The Lion Nov 09 '10 at 09:54
  • +1 Or a container of smart pointers (boost::shared_ptr or std::tr1::smart_ptr). – Klaim Nov 09 '10 at 09:54
  • I know I never call delete on them, but they are still in the list, if I were to call delete at the end of the for loop, my pointers in the list point to garbage, no? – Tony The Lion Nov 09 '10 at 10:01
  • @Tony: Yes but it is error prone and requires unneeded code. You really should give a try to `boost::shared_ptr` it was designed for this specific task. – ereOn Nov 09 '10 at 10:05
  • @Tony: you just need to understand the lifetime of your Request objects. There's nothing wrong with your for loop that's creating them. After you've finished using them, but before the vector itself goes out of scope, make sure you iterate over the vector again and delete each pointer. It's that simple. Suggestions like "ur"'s help guarantee that's done reliably by tying the loop to delete the pointers to the vector's lifetime. – Tony Delroy Nov 09 '10 at 10:06
  • @Tony: That is true. You just have to take care, because when that list gets destroyed, the objects the pointers point to will not get deleted automatically, and you will leak memory. To prevent this, either delete them manually, or use a pointer container, like I suggest in my answer. – Björn Pollex Nov 09 '10 at 10:07
0

If you are not able (or allowed) to use smart pointers, probably you could make use of a simple memory manager like this:

template <class T>
class MemManager
{
public:
  typedef std::vector<T*> Vec;
  ~MemManager ()
  {
    size_t sz = v_.size ();
    for (size_t i = 0; i < sz; ++i)
      delete v_[i];
  }
  T* pushNewObject () 
  {
    T* t = NULL;
    try
    {
        t = new T;
        if (t != NULL)
           v_.push_back(t);            
    }
    catch (std::bad_alloc& ex) { /* handle ex */ }
    return t;
  }
  const Vec& objects() const { return v_; }
private:
  Vec v_;
};

// test
{
  MemManager<Request> mm;
  for (int i = 0; i < 5; i++)
    {
      std::stringstream ss;
      ss << "elemenent ";
      ss << i;

      std::string s = ss.str();

      Request* req = mm.pushNewObject();
      req->name = s;    
    }
} // all Request objects will be deleted here when 
  // the MemManager object goes out of scope.
Vijay Mathew
  • 26,737
  • 4
  • 62
  • 93
  • This leaks too. (Read on Big Three and consider exception safety) – Matthieu M. Nov 09 '10 at 10:39
  • @Matthieu Could you please explain how/where this leaks? – Vijay Mathew Nov 10 '10 at 05:11
  • @Vijay: What happens if `push_back` fails ? Admittedly it should only occur because of a `std::bad_alloc`, meaning either that you are out of memory, or simply that the `vector` has grown too much and no block of sufficient size may be found any longer. There is also the issue with `MemManager` being copyable, you should forbid copy and assignment. Finally, on an interface note: what's the point of `newObject` ? I'd say either let the user deal with the memory (hiding `new` doesn't give much), or provide a full wrapper which will allocate and register the object at the same time :) – Matthieu M. Nov 10 '10 at 07:41
  • @Matthieu Yes you are right. Updated the answer with one possible fix. – Vijay Mathew Nov 10 '10 at 16:16
  • @Matthieu My intention was just to present an idea, not to provide a foolproof solution, which in C++, takes lot of care and conformance to many iron rules. – Vijay Mathew Nov 11 '10 at 03:50
  • @Vijay: I understand your intention, however most people will trust the code (out of laziness) or fail to spot the error, and then use the pattern in their code confident that this time it'll work... You don't have to foolproof it, but I do think that the community has the duty to indicate the caveats to the unwary. That's what the comments are from, aren't they :) ? I do like the idea of superposing RAII on top of an existing component by the way, and use it myself for various tasks. – Matthieu M. Nov 12 '10 at 07:37
0

I'll consider that you have a loop, at the end of the method, to call delete on each member of the vector.

There are still issues, specifically exception safety issues.

  • If anything throws between the creation of the Request and its registration in the vector, you've lost the memory. One solution is to temporarily use a scoped_ptr to hold on the memory, push_back with ptr.get() and then call the release method since now the memory is owned by the vector.
  • If anything throws between the point when you have created the items in the vector and the point you destroy them, you need to catch the exception, destroy the items, and then rethrow.

There might be others, but RAII has been invented for a reason, it's really difficult to do without (correctly...)

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
0

If you cannot use smart pointers, then use boost::ptr_vector.

Note that if you are using TinyXml, memory management in XmlNode is probably dictated by the library - recent history iirc is that many of your problems are associated with properly understanding the memory ownership and release paradigm for this library.

What memory management do I need to cleanup when using TinyXml for C++?

What is the best open XML parser for C++?

Community
  • 1
  • 1
Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
-1

A quick improvement could be to derive a class RequestVector from std::vector<Request*>, add a ClearRequests method (which deletes all the Request objects and clears the vector) and and make it's destructor call ClearRequests. (Actually aggregating the vector in RequestVector could be a better choice, but a derived class is faster done).

ur.
  • 2,890
  • 1
  • 18
  • 21
  • 2
    -1: [Inheriting from STL-Containers is discouraged](http://stackoverflow.com/questions/2034916/is-it-okay-to-inherit-implementation-from-stl-containers-rather-than-delegate). You should use composition for what you propose. – Björn Pollex Nov 09 '10 at 10:19
  • I like inheriting from STL containers. It's perfectly safe if you're not passing around pointers to their base classes, which you wouldn't even even consider doing in 99.9% of cases. How often do you store pointers to containers? And how many of those times would you even consider using a pointer to the base class? How often are your containers on the heap anyway? They're overwhelmingly on the stack or member data. It's FUD. – Tony Delroy Nov 09 '10 at 10:45