I want to append an element to a list without copying any already existing variable.
int some_big_int = 123456789;
struct heavy_struct {
std::vector<double> vec(some_big_number, 0);
};
std::list<heavy_struct> my_list;
heavy_struc new_item;
my_list.push_back(new_item);
If I understand correctly this is what happens:
heavy_struct instance is created called new_item
my_list creates a new heavy_struct instance in which new_item is copied
Is it possible to just append a new heavy_struct, without copying already existing structure?
This copying process is heavy and useless in this case.
Yes:
my_list.emplace_back();
Will pass its arguments (none in this case) to the heavy_struct object being constructed in-place in the list.
You could use a list of pointers to your heavy struct, thus changing the heavy deep copy to a pointer asignment
std::list<heavy_struct*> my_list;
If you don't want to handle memory management either, go with shared pointers instead of raw ones.
Related
Im using a std::deque to hold some objects, and it works great as long as I can add new elements with deque.emplace_front. However, now I want to replace an element of the deque with an already existing object. When I try to do the following
auto it = mydeque.begin();
++it;
mydeque.insert(it, object);
mydeque.erase(it);
I get an error because my object does not allow copying. How can I get around this issue?
EDIT:
My object is of type hp::DoFHandler<dim>, documented here: https://www.dealii.org/current/doxygen/deal.II/classhp_1_1DoFHandler.html.
The reason I can not use emplace is because this method constructs a new object, while I want to insert my existing one.
I could create a minimal working example, but since Im using the FEM framework https://www.dealii.org/, it would require either installing it or downloading a Docker image.
The reason I can not use emplace is because this method constructs a new object, while I want to insert my existing one.
The element of a container is always a distinct object. If you insert an existing object, then the object must be copied or moved.
I get an error because my object does not allow copying. How can I get around this issue?
If the type allows moving, then you can use std::move. Othewise, you cannot insert a pre-existing object into the container.
Some XY-solutions for non-movable types:
Avoid the problem by creating the object within the container initially, using emplace.
Use indirection. Instead of storing hp::DoFHandler<dim> objects in the container, rather store something that refers to such object like a pointer for example. This approach has the drawback of having to ensure that the lifetime of the pointer doesn't exceed the lifetime of the pointed object. Using shared ownership (std::shared_ptr) is an easy way to make sure of that, but it has other potential drawbacks.
This question already has answers here:
How can I create objects while adding them into a vector?
(5 answers)
Closed 9 years ago.
This is a pretty straight forward question.
Is there a way to have a vector and initialize an element without constructing and then copying it?
class BigType
{
// has a costly copy constructor
};
int main(void)
{
using std::vector;
vector<BigType> bigTypeVec;
bigTypeVec.push_back(BigType(/*constructor from parameters*/));
// This constructs a temp object, and then copies it to the new element.
}
Of course there are all sorts of work-a-rounds involving vectors of pointers, or instead of using a constructor, initialize an element's components with set functions, however I was wondering if there were a way to do it so that it can call the constructor on the element it allocates during push_back.
Edit: This question was marked as a duplicate, however I had viewed that page and the answers to his question hadn't answered mine. I want to know how to set the value of the element by constructing it once, rather then copy constructing a temporary object into the element. Emplace was a good way to do this.
You could use std::vector<...>::emplace() or, if you want to append the object, std::vector<...>::emplace_back(), to construct the object in place. If you have to use C++03 this member function isn't available. As an approximation you could push_back() and empty object and then swap() your BigType into the corresponding location, assuming you could construct a small empty object.
Note that std::vector<...> isn't necessarily the best data structure if you have huge objects: if the reserved spaces runs out, the vector need to shuffle the objects around to make new space. You might want to use std::deque<...> instead, as it won't leave its objects put (unless you insert into the middle) while having similar access characteristics as std::vector<...>.
with C++11, yes.
bigTypeVec.emplace_back(BigType());
Here's some more info:
http://en.cppreference.com/w/cpp/container/vector/emplace_back
"Is there a way to have a vector and initialize an element without constructing and then copying it?"
Yes.
Consider placement new as a mechanism to side-step the use of a copy assignment and it's use of a temporary.
"Is there a way to have a vector..."
A vector can be built with elements created with the default constructor.
I believe it is possible to define BigType so that no element initialization is required during the bigTypeVec construction. In this way, declaring the vector (or even a simple array) will trigger no element constructor work. Consider these:
vector<BigType> bigTypeVec;
or
BigType bigTypeVec[MAX_BigTypeVecSize];
Note that the array requires BigType to provide a default constructor (or you to provide a big bunch of curly brace array initialzation).
However, I can imagine that you might find value for each BigType element to have an indication that it is or is not initialized.
" and initialize an element without constructing [a temp] and then copying it [, the temp, to the vector]?"
Placement new can then be used to construct the object in place. By passing the address of the desired bigTypeVec element you wish to initialize to the placement new, all the element constructor work will occur where (in memory) you want it. Consider something like:
vector<BigType> bigTypeVec;
BigType* pBT = 0;
pBT = new (&bigTypeVec[0] BigType(<param>); // placement new
pBT = new (&bigTypeVec[1] BigType(<param>);
...
pBT = new (&bigTypeVec[n] BigType(<param>);
Note the discard of pBT. Pointer is not used.
*"I was wondering if there were a way to do it so that it can call the constructor on the element it allocates during push_back."*
At this point, all that remains is to create the simplest class possible that inherits from std::vector() and re-impliments "push back", or perhaps a new method that supports your needs. These methods would seek the vector element that push-back would have found, and use the placement new similar to above.
Are you sure about this?
"This constructs a temp object, and then copies it to the new element."
As far as I know, it will directly create an object at the memory location. Return value optimization. Temporary will not be created.
Am I wrong?
So I have some C++ classes that use a map and key class for a sort of data structure. In my insert method I use the typical map.insert. I want this function to return a pointer so I can modify some values (not the one used for comparison) inside the element inserted. So I was wondering if this is safe to this..
template<typename T>
NodeT<T> * TreeT<T>::
MakeNode(PointT point)
{
NodeT<T> * prNode = new NodeT<T>;
//set the contents for the node
prNode->SetNode(point, m_dTolerance);
//Create the key class using the
VectorKey key(point, m_dTolerance);
//Store the key,node as a pair for easy access
return_val = m_tree.insert( pair<VectorKey, NodeT<T> >(key, *prNode) );
if (return_val.second == false)
//if return_val.second is false it wasnt inserted
prNode = NULL;
else
//it was inserted, get a pointer to node
prNode = &(return_val.first->second); //is this safe if I plan to use it later?
return prNode;
}
I seemed to learn the hard way that my original pointer (the one I created with new), was pointing to the wrong element after the insert. Can anyone tell me why that is? So I used the return_val iterator to get the right pointer. I kinda dont want to return an iterator but if its safer then I do...
Thanks!
You seems to have troubles in your code with pointers and values. First you allocate an object on a heap ( with new Node )
Then you use a copy of that object to sore within your map.
PS. And then you loose original object forever as do not free memory which leads to memory leak.
In your case - it is invalid because you return pointer to object which can be deleted at any time ( for example next time you add something to your map and map decides to reallocate it's tree, so it will copy objects to different places ).
Storing pointers as map values prevents this. The only thing you need to remember to clear them up when removing object from map and when removing map itself.
The easy way to handle that would be using smart pointers (boost::shared_ptr for example ) or smart map class (boost::ptr_map for example ).
To fix that - use pointers everywhere ( store a pointer as a map value ).
This way - you will be able to return pointer from this function and it will be valid.
So just turn your map to map*> and this should fix most of your problems.
Do not forger to delete objects when erasing them from the map.
This code sample is interesting because contains a several things wrongs or to avoid.
Implementation
The most important things are been said (mainly by Bogolt):
You are leaking memory, because allocate NodeT<T> from the heap and never free it again, since map will allocate a copy of the object, not the pointer. Indeed, you specify as parameter *prNode, not prNode.
You use the heap to allocate the object (will be copied into the map), but you assume you always allocate the object. Despite it will be the very most probably case, that is not alway true: new operator would be return null or throw a bad_alloc exception. The code does not handle it.
Anyway, you use the heap when is not really needed. (And you see the problems are you intriducing because that). You can just create the object in the stack and then insert into the map, avoiding the previous problems and typing less code.
Design
The function returns a pointer to the element in the map. Depending the program, is possible this is safe. But what happens if the code reference the pointer when the object is removed from the map? Better, if you are returning pointer, do not return a raw pointer. Use smart pointer (shared_ptr in this case) instead. Using shared_ptr you will have not problems with the object life.
Other reason to use smart pointers: because the insertion into the map imply a copy of the element, you are imposing a requirement to NodeT<T>: it has to be copy constructible. May be this requirement is not important for performance, but may be in other circumstances copying the object have drawbacks. If you use smart pointer (or boost::ptr_map), the object will be created just once and is not copied.
Style
Just some suggestion, but not too important:
instead type pair<VectorKey, NodeT<T> >(key, *prNode), type make_pair(key, *prNode). The code is more compact and clearer typing less.
Well I'd say that depends on is your map alive longer than anything that could use (and store) the pointer.
If yes, (ie, it's in some sort of singleton), you could go with it, but not very safe anyway since any code could delete the pointer.
The best option is to store boost::shared_ptr (or std:: since c++11) instead of raw pointers in your mapn and return only a weak_ptr after insertion.
That way, you're sure no other code can delete your pointer as long as the map holds it, and that no one can use a pointer that has been erased from the map.
This is presumable a simple C++ question, but I'm relearning C++ and don't know some of the basics. I have a class that includes a struct with a vector of objects in it, so something like this:
struct my_struct{
Irrelevant_Object object,
vector<tuple> tuple_list;
}
The struct and the tuple (another struct) are predefined by the architecture and given to me in my method; so I can't change them. I want to generate and insert a tuple into the originaly empty tuple_list.
The simple solution is have a method which allocates a new tuple object, fills in the tuple data, then call tuple_list.push_back() and pass in the allocated tuple. But this would require allocating a new tuple only to have the push_back method copy all of the contents of the (large) tuple struct into an already defined memory space of the vector. So I'm paying the expense of an allocation/delete as well as the lesser expense of copying the tuple contents into the vector to do it this way. It seems rather inefficent, and since this method would be in the critical path of the function I would prefer something faster (admitedly I doubt this method would be the bottle-neck, and I know early optimization == bad. However, I'm asking this question more to learn something about C++ syntax then out of a deperate need to actually do this in my code).
So my question is, is there a quicker way to fill the contents of my tuple list without allocating and copying a tuple? If this was an array I could make the array as large as I want, then past a reference to tuple_list[0] to the function that creates the tuple. That way the funciton could fill the empty contents of the already allocated tuple within the array without allocating a new one or copying from one tuple to another. I tried to do that with the vector out of curiousity and ended up with a seg fault when my itterator pointed to 0x0, so I assume that syntax doesn't work for vectors. So is there a quick way of doing this assignment?
Since this is a question as much to learn the language as for actual use feel free to throw in any other tangentally relevant stuff you think are interesting, I'm looking to learn.
Thanks.
In C++11, you can use std::vector::emplace_back, which constructs the new object in-place, therefore there is no copying when you use this method.
By using this method, you could do this:
my_struct some_struct;
some_struct.tuple_list.emplace_back(1, 5, "bleh");
Assuming your tuple object contains this constructor:
tuple::tuple(int, int, const std::string&)
Edit: You can also use move semantics to store a pre-allocated tuple:
my_struct some_struct;
tuple a_tuple;
/* modify a_tuple, initialize it, whatever... */
some_struct.push_back(std::move(a_tuple)); // move it into your vector
Or use a reference to the tuple after it has been stored in the vector:
my_struct some_struct;
some_struct.tuple_list.emplace_back(1, 5, "bleh");
// store a reference to the last element(the one we've just inserted)
tuple &some_tuple = some_struct.tuple_list.back();
some_tuple.foo();
On all of the above solutions you're creating only one tuple while also avoiding copying.
How could I free up a linked list that contains dynamically allocated objects?
I try to use list<class*> lists, but then I could not use the insert() function to insert object to the list. Does anyone know what is the cause?
std::list<boost::shared_ptr<YourType> > will automatically call YourType::~YourType on each (smart) pointer in the list, when the list is deleted. And if you erase one list element, it will call YourType::~YourType for that one element. You can still call list.insert(new YourType)
Link: www.boost.org
how could I free up linked list that contain dynamically allocated object?
Walk the list, deleting the contained object for each link, or better in some cases, write a link dtor that deletes the contained object.
I try to use list lists, but then I could not use insert()function to insert object to the list. Does anyone know what is the cause?
Not without seeing your code.
Traverse the list from the beginning to end, and delete one by one.
The solution used by the STL is to have the container own the objects it contains. In this scenario each node would be responsible for deallocating it's object when it's destructor is called. You're still responsible for deallocating the copy of the object you pass in.
Edit
If you're attempting to use the stl List and encountering an error, it might be the case that the object you're trying to place inside does not implement a copy constructor. This can happen if you try to stick things other than pointers into the list. If you're comfortable with pointers you might consider storing them in your containers instead of the objects themselves. This will require that you call 'delete' yourself on the pointer when you're done with it. This is typically done by calling 'delete' on the pointer contained in a node when you remove it or by walking the list when you're done with it and calling 'delete' on the contents of each node.
You will have to iterate the list and delete the dynamically allocated objects, before clearing the list.
When using containers of newed
pointers, please remember to delete
the pointers before the container is
destroyed.
About problem using insert() : I am not very sure what is the problem you are facing. From the explanation, I assume, you are trying to insert objects of Class into list where as the list contains the pointer to Class. Could you please elaborate the problem with insert?
Pseudo code for deleting the dynamically allocated objects:
version 1: Simple
list<Class*>::const_iterator iter = m_ClassList.begin();
list<Class*>::const_iterator endIter = m_ClassList.end();
for(;iter!= endIter ; ++iter)
{
delete *iter;
}
ClassList.clear();
version 2: A better version using for_each
struct DeleteClassObject
{
//Functor
template<typename T>
void operator()(const T* ptr) const
{
delete ptr;
}
}
//loops through list and deletes the dynamically allocated objects using
//functor DeleteClassObject
for_each( m_ClassList.begin(), m_ClassList.end(), DeleteClassObject ());
DeleteClassObject.clear()
You could use ptr_list from Boost
http://www.boost.org/doc/libs/1_38_0/libs/ptr_container/doc/ptr_list.html
EDIT: THIS IS WRONG, SORRY
If you're using std::list then there's a clear function to delete all objects you put in.
std::list test;
test.clear();
Oh, and .clear() also invokes the destructors.
To use the insert function of such lists you need to specify the position via an iterator.
test.insert(test.end(), 5);
But there's more than 1 insert function, here you can find more details
EDIT
Could somebody leave a comment when downvoting?
Start your project in language/environment, which having an automatic memory management (Garbage collection). Then things like that will be the least which you care about. It will save your time to put your efforts into something else.