Why Does Storing References (Not Pointers) in Containers in C++ Not Work

Why does storing references (not pointers) in containers in C++ not work?

Containers store objects. References are not objects.

The C++11 specification clearly states (§23.2.1[container.requirements.general]/1):

Containers are objects that store other objects.

Containers in C++: pointers vs references

You maybe saw "T& at(key)" for your map, or perhaps something else that talked about references? Containers do take values as references, but that's just for efficiency. They are then copied into the container.

If you decide to put values into the container (option 1), then your item has to be copied when inserted, but then can be modified by reference:

blah &a = map1["a"]
a.foo = somethingelse;
// No need to do this: map1["a"] = a;

Or, shorter:

map1["a"].foo = somethingelse;

When you do this with values, the map owns the object and will delete it when the map is deleted (among other times).

If you store raw pointers, you must manage the memory. I wouldn't advise that. I would instead consider putting shared_ptr or unique_ptr into your map. You do that if you need to have the value outside of the map stay alive even if the map is destroyed.

map<string,shared_ptr<blah>> map3;
shared_ptr<blah> myPtr = make_shared<blah>();
map3["a"] = myPtr;

Here, I can still use myPtr even after the map goes away. After everything pointing to the object is gone, the object will be deleted.

STL containers with reference to objects

Sadly no, it won't compile (with stlport at least). But the alternative, which is to store pointers to your objects in the container, will compile perfectly fine.

This will leave you with a bit of extra syntactic noise around your code - you'll have to new things in order to insert them into your container.

std::list<class_type*> l;
l.push_back(new class_type);

However though the objects now won't be copied, they also won't be automatically cleaned up for you when the list is destructed. Smart pointers will solve this for you, but at the cost of even more syntactic noise. And since you can't put std::auto_ptr's in standard containers because they can't be copied, you have to use their slightly heavier-weight boost cousins, shared pointers.

std::list<boost::shared_ptr<class_type> > l;
l.push_back(boost::shared_ptr<class_type>(new class_type));

Shared pointed do incur some extra overhead, but it is minimal.

Why cannot structs storing plain pointers to internal memory be stored in stxxl containers?

(including pointers) to internal memory

This means a pointer to a member of the struct, or otherwise a pointer into the memory that the container manages. E.g. you have

struct Foo {
int *a;
int b;
};

Foo f;
f.a = &f.b

Since f.a now points to a member of the struct, and that struct could be copied around, the pointer can be invalid. Similar, if the pointer points to any other struct Foo managed by the container- which could be moved around too.

If you just have a pointer, and manage what it points to, you should be fine.

Storing references with std:: containers , gnu c++98

When you write code like this:

objVec.push_back(*new TestClass(10, 100));

you are creating a new instance of TestClass on the heap, then you are dereferencing it using *, and then this is copied inside the vector when you call push_back.

But you are leaking the original TestClass object allocated using new on the heap.

You may want to use a vector<shared_ptr<TestClass>> or vector<unique_ptr<TestClass>> instead if you want to store pointers (smart pointers) instead of TestClass instances (are you sure?).

Note that a vector of references would be vector<TestClass&>, which is wrong.

P.S. As you cited C++98" in the title, you can't have unique_ptr as it requires C++11 move semantics. shared_ptr became standard with C++11; you can still use boost::shared_ptr in C++98.

How to store references to other objects in C++?

When discussing "Best Practices", it's important to consider what your quality-attributes and needs are for the code.

There is no "right" or "wrong" answer in the example of code such as a Graph; there are varying degrees that solve different problems in different ways -- and it depends strongly on the way its intended to be used.

By-far the simplest way to solve such a problem is for the main container (Graph) to have strong ownership in the with unique_ptr, and to only view the lifetime in the internal elements (Node) with a raw pointer, e.g.:

class Graph
{
...
private:
std::vector<std::unique_ptr<Node>> m_nodes;
};

class Node
{
...
private:
std::vector<const Node*> m_connected_nodes;
};

This would work well, since Node cannot mutate its connected nodes, and since Graph assumes that Node will never outlive it.

However, this approach does not work if you ever want Node to outlive Graph, or if you want Node to be used across multiple Graph objects. If it lives between different Graphs, then you may run the risk of a Node referring to a dangling pointer -- and this would be bad.

If this is the case, you might need to consider a different ownership pattern, such as shared_ptr and weak_ptr ownership:


class Graph
{
...
private:
std::vector<std::shared_ptr<Node>> m_nodes;
};

class Node
{
...
private:
std::vector<std::weak_ptr<const Node*>> m_connected_nodes;
};

In this case, Nodes only weakly know other Node objects, whereas Graph is the strong owner of them. This prevents the dangling issue, but incurs additional overhead now for the shared_ptr's control node, and for having to check for whether it's alive before accessing weak_ptr nodes.

So the correct answer is: It depends. If you can get away with the former approach, that's probably the cleanest; you always have 1 owner, and thus the logic is simple and easy to follow.

Should I store entire objects, or pointers to objects in containers?

Since people are chiming in on the efficency of using pointers.

If you're considering using a std::vector and if updates are few and you often iterate over your collection and it's a non polymorphic type storing object "copies" will be more efficent since you'll get better locality of reference.

Otoh, if updates are common storing pointers will save the copy/relocation costs.



Related Topics



Leave a reply



Submit