The Standard Template Library documentation for list says:
void push_back ( const T& x );
Add element at the end Adds a new element at the end of the list, right after its current last element. The content of this new element is initialized to a copy of x.
These semantics differ greatly from the Java semantics, and confuse me. Is there a design principle in the STL that I'm missing? "Copy data all the time"? That scares me. If I add a reference to an object, why is the object copied? Why isn't just the object passed across?
There must be a language design decision here, but most of the commentary I've found on Stack Overflow and other sites focuses on the exception throwing issues associated with the fact that all this object copying can throw exceptions. If you don't copy, and just handle references all those exception problems go away. Very confused.
Please note: in this legacy code base I work with, boost is not an option.
Lists are sequence containers that allow non-contiguous memory allocation. As compared to vector, the list has slow traversal, but once a position has been found, insertion and deletion are quick. Normally, when we say a List, we talk about a doubly linked list.
In vector, each element only requires the space for itself only. In list, each element requires extra space for the node which holds the element, including pointers to the next and previous elements in the list.
Without reference-counting there is no way to maintain shared-ownership, so single-ownership is maintained by copying.
Consider the common case where you want to add a stack-allocated object to a list that will outlive it:
void appendHuzzah(list<string> &strs) {
strs.push_back(string("huzzah!"));
}
The list can't keep the original object because that object will be destroyed when it falls out of scope. By copying, the list obtains its own object whose life-span is entirely under its own control. If it were otherwise, such straightforward usage would crash and be useless and we would always have to use lists of pointers.
Java distinguishes between primitive types and reference types. In C++ all types are primitive.
You don't add a reference to an object. You pass an object by reference. That's different. If you didn't pass by reference, an extra copy might have been made even before the actual insert.
And it does a copy because you need a copy, otherwise code like:
std::list<Obj> x;
{
Obj o;
x.insert(o);
}
would leave the list with an invalid object, because o
went out of scope. If you want something similar to Java, consider using shared_ptr
. This gives you the advantages you're used to in Java - automatic memory management, and lightweight copying.
The STL always stores exactly what you tell it to store. list<T>
is always a list of T
so everything will be stored by-value. If you want a list of pointers, use list<T*>
, that will be similar to the semantics in Java.
This might tempt you to try list<T&>
, but that is not possible. References in C++ have different semantics than references in Java. In C++, references must be initialized to point to an object. After a reference is initialized, it will always point to this object. You can never make it point to a different object. This makes it impossible to have a container of references in C++. Java references are more closely related to C++ pointers, so you should use list<T*>
, instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With