3

I've had some experience in C++ from school works. I've learned, among other things, that objects should be stored in a container (vector, map, etc) as pointers. The main reason being that we need the use of the new-operator, along with a copy constructor, in order to create a copy on the heap (otherwise called dynamic memory) of the object. This method also necessitates defining a destructor.

However, from what I've read since then, it seems that STL containers already store the values they contain on the heap. Thus, if I were to store my objects as values, a copy (using the copy constructor) would be made on the heap anyway, and there would be no need to define a destructor. All in all, a copy on the heap would be made anyway???

Also, if(true), then the only other reason I can think of for storing objects using pointers would be to alleviate resource needs for copying the container, as pointers are easier to copy than whole objects. However, this would require the use of std::shared_ptr instead of regular pointers, since you don't want elements in the copied container to be deleted when the original container is destroyed. This method would also alleviate the need for defining a destructor, wouldn't it?

Edit : The destructor to be defined would be for the class using the container, not for the class of the objects stored.

Edit 2 : I guess a more precise question would be : "Does it make a difference to store objects as pointers using the new-operator, as opposed to plain values, on a memory and resources used standpoint?"

Matzar
  • 243
  • 2
  • 9
  • 3
    Objects should typically be stored in a container plain. – chris May 18 '13 at 04:53
  • 6
    Most of what you've said is either incorrect or only partly correct. Defining a destructor for `T` doesn't mean a `vector` will automatically delete the objects when it's done using them. Also, with C++11 and move semantics, concerns about objects in containers being copied too many times is moot. About the only time you want to store a `T*` in a container instead of a `T` should be when that `T` is non-copyable and non-moveable due to restrictions of some data member. Finally, what exactly is your question? – Praetorian May 18 '13 at 04:57
  • 2
    @Praetorian: "About the only time you want to store a T* in a container" => or for polymorphism (pointer to base class). But in any case one should avoid storing naked pointers in containers and prefer smart pointers instead. – syam May 18 '13 at 05:04
  • A pointer is an object. That is, both objects and pointers are just a number of bytes. See this wonderful lecture for more information: https://www.youtube.com/watch?v=Ps8jOj7diA0 – Homer6 May 18 '13 at 05:08
  • @Praetorian: "Defining a destructor for T doesn't mean a vector will automatically delete the objects when it's done using them." => I meant defining a destructor for the class using the vector, not the class of the objects stored. – Matzar May 18 '13 at 05:14
  • @syam: I am indeed using it for polyphormism. – Matzar May 18 '13 at 05:15
  • @Homer6: Thank you, but I already learned that when I was learning assembly. :P – Matzar May 18 '13 at 05:16
  • 1
    @Matzar And how does that help destroy the objects contained within the `vector`? Polymorphism is another legitimate reason that I forgot about. But please use `vector>` instead of `vector` – Praetorian May 18 '13 at 05:17
  • @Matzar: yes in this case you don't have the choice, you can't store plain objects in a container (or you'd hit [the infamous object slicing problem](http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c)). Smart pointers are the most robust solution then, with the big advantage that you don't have to handle deletion by yourself. – syam May 18 '13 at 05:18
  • @Praetorian: Well it helps if I store them as simple pointers. Otherwise destruction of the vector (or map) by the class using it would cause memory leaks (the objects pointed to not being destroyed). – Matzar May 18 '13 at 05:25
  • @syam: Yeah! I can't believe I've never even heard of smart pointers before earlier today. – Matzar May 18 '13 at 05:27
  • @Matzar Say you have a type `U` that contains a `vector`. Defining `~U()` doesn't do *anything* as far as the vector destroying the objects it contains goes. The `vector` *always* calls the destructors of the type it contains, in this case it'll call the *destructor* for `T*`, which is a NOP. If the objects it contained were `new`ed when originally created, you've leaked memory. This is where smart pointers are invaluable. A `vector>` will always `delete` the `T*` when necessary. I don't understand what you mean by *Well it helps if I store them as simple pointers* Helps how? – Praetorian May 18 '13 at 05:28
  • @Praetorian: By defining the destructor to manually destroy all the objects pointed by the pointers contained in the vector, using iterators and the delete-operator. Ex: `GameObjectManager::~GameObjectManager() { for (std::map::iterator it = m_vGameObjects.begin(); it != m_vGameObjects.end(); it++) { delete (*it).second; } }` – Matzar May 18 '13 at 05:35
  • 2
    @Matzar Ah, now I get it. That works, but you don't need to write that if you use a container holding smart pointers instead. Always strive to write classes that do not need a destructor to be explicitly defined. Or copy/move constructors/assignment operators for that matter. Please read this post titled [rule of zero](http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html). – Praetorian May 18 '13 at 05:41
  • @Praetorian: Yeah, I'm really glad I now know about smart pointers. – Matzar May 18 '13 at 05:49

1 Answers1

6

The main reason to avoid storing full objects in containers (rather than pointers) is because copying or moving those objects is expensive. In that case, the recommended alternative is to store smart pointers in the container.

So...

vector<something_t> ................. Usually perfectly OK
vector<shared_ptr<something_t>> ..... Preferred if you want pointers
vector<something_t*> ................ Usually best avoided

The problem with raw pointers is that, when a raw pointer disappears, the object it points to hangs around causing memory and resource leaks - unless you've explicitly deleted it. C++ doesn't have garbage collection, and when a pointer is discarded, there's no way to know if other pointers may still be pointing to that object.

Raw pointers are a low-level tool - mostly used to write libraries such as vector and shared_ptr. Smart pointers are a high-level tool.

However, particularly with C++11 move semantics, the costs of moving items around in a vector is normally very small even for huge objects. For example, a vector<string> is fine even if all the strings are megabytes long. You mostly worry about the cost of moving objects if sizeof(classname) is big - if the object holds lots of data inside itself rather than in separate heap-allocated memory.

Even then, you don't always worry about the cost of moving objects. It doesn't matter that moving an object is expensive if you never move it. For example, a map doesn't need to move items around much. When you insert and delete items, the nodes (and contained items) stay where they are, it's just the pointers that link the nodes that change.

  • Well, thanks a lot! I wish I could upvote. :) That's pretty much what I had gathered regarding memory allocation and pointers. But I'm still a bit lost on the matter of the size of objects (to know if copying/moving the objects in question will be costly). I'll have to do more research. – Matzar May 18 '13 at 10:10