I have an issue with creating objects, adding them to a container class, and having them go out of scope in C++.
As an example, my main.cpp
Container container;
for (unsigned int i = 0; i < 10; i++) {
Item item;
container.add_item(item);
}
and the interface of container.h
struct Container {
std::vector<std::reference_wrapper<Item>> items;
void add_item(Item& item); // Push back of items vector.
};
and item.h
struct Item {
std::unique_ptr<AnotherThing> unrelated_thing;
};
The problem is later on in my main class, the Containers created inside the for-loop have gone out of scope. If I change add_item to pass by value, it gives me issues with the unique_ptr and copy constructor.
Is there some idiomatic way to create objects inside a scope and "transfer" them to another class?
If I change add_item to pass by value, it gives me issues with the unique_ptr and copy constructor.
Your vector need to hold objects by value:
std::vector<Item> items;
then you need to move your object into vector when passed by value:
void Container::add_item( Item item )
{
items.push_back( std::move( item ) );
}
and then you need to move your object in the loop as well:
for (unsigned int i = 0; i < 10; i++) {
Item item;
container.add_item( std::move(item) );
}
or you can simply pass temporary:
for (unsigned int i = 0; i < 10; i++) {
container.add_item( Item() );
}
There are a few ways to handle this. This is basically the exact use-case for passing xvalues. Passing by value will cause a copy of all members of item. If there are members that could have large amounts of data allocated on the heap, such as a std::vector, you will want to avoid this copy and allocation by moving your members.
The simple answer is to pass by what Scott Meyers calls a 'Universal Reference'
void Container::add_item( Item&& item) {
items.push_back( std::move(item))
}
Related
Lets take custom vector implementation as an example:
template<typename Object>
class myVector {
public:
explicit myVector(int size = 0) :
_size{ size },
_capasity{ size + SPARE_CAPACITY }
{
_buff = new Object[_capasity];
if (_size > 0) {
for (int i = 0; i < _size; i++) {
//_buff[i] = 0;
}
}
}
// more code
private:
Object * _buff = nullptr;
int _size;
int _capasity;
};
So my question is, how to make myVector be value-initialized in case I'll initialize it as:
int main() {
myVector<int> v02(5);
}
Here, it contains 5 int values, so I need it to be all zeros; same with other types. I commented out _buff[i] = 0; as it's specific to int. Please give me some hints.
It's as simple as
for (int i = 0; i < _size; i++)
_buff[i] = Object{};
Alternatively, you could get rid of the loop and add a pair of {} (or ()) here:
_buff = new Object[_capasity]{};
// ^^
But this option would value-initialize all _capasity objects, rather than the first _size ones, as noted by #bipll.
Also, note that if you want to mimic the behavior of std::vector, you need to allocate raw storate (probably std::aligned_storage) and call constructors (via placement-new) and destructors manually.
If Object is a class type, _buff = new Object[_capasity]; calls default constructors for all _capasity objects, rather than for the first _size objects as std::vector does.
Note that when calling
_buff = new Object[_capasity];
(btw, why have you moved this initialization out of init-list, into constructor body?) you already have default-initialized _capasity objects. Default initialization has the following effects here: while elements of scalar type would remain uninitialized (and reading from them UB), for class types you have already called _capasity constructors.
To avoid unnecessary constructions you have the following possible options, among others:
Use std::aligned_alloc to allocate non-initialized memory:
explicit myVector(std::size_t size = 0) :
size_{ size }
, capacity_{ size + SPARE_CAPACITY }
, buff_{std::aligned_alloc(alignof(Object), _capacity)}
{
if(!buff_) throw std::bad_alloc();
if(size) new (buff_) Object[size]{}; // empty braces answer your original query
}
Remember that again buff_ should be aligned_alloced when vector grows (can be std::realloc()ed for trivial types), and in destructor it should be std::free()d — and prior to that size_ objects inside it should be destructed (with an explicit call to ~Object()).
Change buff_'s type to something more trivial yet properly aligned:
using Storage = std::aligned_storage_t<sizeof(Object), alignof(Object)>;
Storage *buff_;
Object *data_ = nullptr;
public:
explicit myVector(std::size_t size = 0) :
size_{ size }
, capacity_{ size + SPARE_CAPACITY }
, buff_{new Storage(_capacity)}
{
if(size) data_ = new (buff_) Object[size]{};
}
Again, in destructor, objects should be manually destroyed, but this time buff_ can be simply delete[]d afterwards.
Today i went back and investigated an error i got in an old project. It's not exactly an error, rather, i don't know how to do what i need to do. Don't really want to go into the details of the project as it is old and buggy and inefficient and more importantly irrelevant. So i coded a new sample code:
#include <iostream>
#include <vector>
#include <time.h>
#include <random>
#include <string>
class myDoc;
class myElement
{
int myInt;
std::string myString;
myElement * nextElement;
//a pointer to the element that comes immediately after this one
public:
myElement(int x, std::string y) : myInt(x), myString(y){};
friend myDoc;
};//an element type
class myDoc
{
std::vector<myElement> elements;
public:
void load();
~myDoc()
{
//I believe i should delete the dynamic objects here.
}
};// a document class that has bunch of myElement class type objects as members
void myDoc::load()
{
srand(time(0));
myElement * curElement;
for (int i = 0; i < 20; i++)
{
int randInt = rand() % 100;
std::string textInt = std::to_string(randInt);
curElement = new myElement(randInt,textInt);
//create a new element with a random int and its string form
if (i!=0)
{
elements[i-1].nextElement = curElement;
//assign the pointer to the new element to nextElement for the previous element
//!!!!!!!!!!!! this is the part that where i try to create a copy of the pointer
//that goes out of scope, but they get destroyed as soon as the stack goes out of scope
}
elements.push_back(*curElement);// this works completely fine
}
}
int main()
{
myDoc newDoc;
newDoc.load();
// here in newDoc, non of the elements will have a valid pointer as their nextElement
return 0;
}
Basic rundown: we have a document type that consists of a vector of element type we define. And in this example we load 20 random dynamically allocated new elements to the document.
My questions/problems:
When the void myElement::load() function ends, the pointer and/or the copies of it goes out of scope and get deleted. How do i keep a copy that stays(not quite static, is it?) at least until the object it points to is deleted?
The objects in the elements vector, are they the original dynamically allocated objects or are they just a copy?
I allocate memory with new, how/when should i delete them?
Here is a picture i painted to explain 1st problem(not very accurate for the specific example but the problem is the same), and thank you for your time.
Note: I assumed you want a vector of myElement objects where each one points to the element next to it. It is unclear if you want the objects in elements to point to copies of them, anyway it should be pretty easy to modify the code to achieve the latter
This is what happens in your code:
void myDoc::load()
{
..
curElement = new myElement(n,m); // Create a new element on the heap
...
// If this is not the first element we inserted, have the pointer for the
// previous element point to the heap element
elements[i-1].nextElement = curElement;
// Insert a COPY of the heap element (not the one you stored the pointer to)
// into the vector (those are new heap elements copied from curElement)
elements.push_back(*curElement);// this works completely fine
}
so nothing gets deleted when myDoc::load() goes out of scope, but you have memory leaks and errors since the pointers aren't pointing to the elements in the elements vector but in the first heap elements you allocated.
That also answers your second question: they're copies.
In order to free your memory automatically, have no leaks and point to the right elements you might do something like
class myElement
{
int a;
std::string b;
myElement *nextElement = nullptr;
//a pointer to the element that comes immediately after this one
public:
myElement(int x, std::string y) : a(x), b(y){};
friend myDoc;
};//an element type
class myDoc
{
std::vector<std::unique_ptr<myElement>> elements;
public:
void load();
~myDoc()
{}
};// a document class that has bunch of myElement class type objects as members
void myDoc::load()
{
srand((unsigned int)time(0));
for (int i = 0; i < 20; i++)
{
int n = rand() % 100;
std::string m = std::to_string(n);
//create a new element with a random int and its string form
elements.emplace_back(std::make_unique<myElement>(n, m));
if (i != 0)
{
//assign the pointer to the new element to nextElement for the previous element
elements[i - 1]->nextElement = elements[i].get();
}
}
}
Live Example
No need to delete anything in the destructor since the smart pointers will be automatically destroyed (and memory freed) when the myDoc element gets out of scope. I believe this might be what you wanted to do since the elements are owned by the myDoc class anyway.
In my C++ code I have a class Object equipped with an id field of type int. Now I want to create a vector of pointers of type Object*. First I tried
vector<Object*> v;
for(int id=0; id<n; id++) {
Object ob = Object(id);
v.push_back(&ob);
}
but this failed because here the same address just repeats itself n times. If I used the new operator I would get what I want but I'd like to avoid dynamic memory allocation. Then I thought that what I need is somehow to declare n different pointers before the for loop. Straightforward way to this is to declare an array so I did this :
vector<Object*> v;
Object ar[n];
for(int i=0; i<n; i++) {
ar[i] = Object(i);
}
for(int i=0; i<n; i++) {
v.push_back(ar+i);
}
Is there still possibility to get a memory leak if I do it this way? Also going through an array declaration is a bit clumsy in my opinion. Are there any other ways to create vector of pointers but avoid manual memory management?
EDIT: Why do I want pointers instead of just plain objects?
Well I modified the original actual situation a bit because I thought in this way I can represent the question in the simplest possible form. Anyway I still think the question can be answered without knowing why I want a vector of pointers.
Actually I have
Class A {
protected:
vector<Superobject*> vec;
...
};
Class B: public A {...};
Class Superobject {
protected:
int id;
...
}
Class Object: public Superobject {...}
In derived class B I want to fill the member field vec with objects of type Object. If the superclass didn't use pointers I would have problems with object slicing. So in class B constructor I want to initialize vec as vector of pointers of type Object*.
EDIT2
Yes, it seems to me that dynamic allocation is the reasonable option and the idea to use an array is a bad idea. When the array goes out of scope, things will go wrong because the pointers in vector point to memory locations that don't necessarily contain the objects anymore.
In constructor for class B I had
B(int n) {
vector<Object*> vec;
Object ar[n];
for(int id=0; id<n; id++) {
ar[id] = Object(id);
}
for(int id=0; id<n; id++) {
v.push_back(ar+id);
}
}
This caused very strange behavior in objects of class B.
In this loop:
for(int id=0; id<n; id++) {
Object ob = Object(id);
v.push_back(&ob);
}
You are creating n times Object instance on stack. At every iteration there is created and removed element. You can simply avoid this using that:
for(int id=0; id<n; id++) {
Object* ob = new Object(id);
v.push_back(ob);
}
thanks that every new element exist on heap not on the stack. Try to add to in class Object constructor something like that:
std::cout<<"Object ctor()\n";
and the same in the destructor:
std::cout<<"Object dtor()\n";
If you dont want to create these objects with "new" try reason written by #woolstar
Your question about memory leaks makes me think you are worried about the lifecycle and cleanup of these objects. I originally proposed shared_ptr wrappers, but C++11 gave us unique_ptr, and C++14 filled in the missing make_unique. So with all that we can do:
vector<unique_ptr<SuperObject>> v ;
Which you create in place with the wonderfulness of perfect forwarding and variadic templates,
v.push_back( make_unique<Object>( ... ) ) ;
Yes you are going to have to live with some heap allocations, but everything will be cleaned up when v goes away.
Someone proposed a boost library, ptr_container, but that requires not only adding boost to your project, but educating all future readers what this ptr_container is and does.
No there is no memory leak in your version. When the program leaves your scope the vector as well the array are destroyed. To your second question: Why not simply store the objects directly in an vector?
vector<Object> v;
for(int i = 0; i < n; i++)
{
Object obj = Object(i);
v.push_back(obj);
}
Is it safe to return a vector that's been filled with local variables?
For example, if I have...
#include <vector>
struct Target
{
public:
int Var1;
// ... snip ...
int Var20;
};
class Test
{
public:
std::vector<Target> *Run(void)
{
std::vector<Target> *targets = new std::vector<Target>;
for(int i=0; i<5; i++) {
Target t = Target();
t.Var1 = i;
// ... snip ...
t.Var20 = i*2; // Or some other number.
targets->push_back(t);
}
return targets;
}
};
int main()
{
Test t = Test();
std::vector<Target> *container = t.Run();
// Do stuff with `container`
}
In this example, I'm creating multiple Target instances in a for loop, pushing them to the vector, and returning a pointer to it. Because the Target instances were allocated locally, to the stack, does that mean that the returned vector is unsafe because it's referring to objects on the stack (that may soon be overwritten, etc)? If so, what's the recommended way to return a vector?
I'm writing this in C++, by the way.
Elements get copied when you push_back them into a vector (or assign to elements). Your code is therefore safe – the elements in the vector are no references to local variables, they are owned by the vector.
Furthermore, you don’t even need to return a pointer (and never handle raw pointers, use smart pointers instead). Just return a copy instead; the compiler is smart enough to optimise this so that no actual redundant copy is made.
#include <QList>
class MyType{
//This has some data in it....
};
QList<MyType> f()
{
QList<MyType> list;
for(int i = 0; i<10; i++ )
{
MyType* item = new MyType();
list << *item;
}
return list;
}
QList<MyType> temp_var = f();
When temp_var goes out of the scope and destroys, what happens to the items that we created and add to this list?
Is there going to be any memory leaks?
Thank you.
Yes, there will be a memory leak. As a general rule, you must have one delete for each new in your program.
In your specific case, the faulty logic happens much earlier than temp_var's destruction. You allocate the items, and then store a copy of those items in the list. You should immediately destroy the original, no-longer-useful items.
Your for loop could be :
for(int i = 0; i<10; i++ )
{
MyType* item = new MyType(); // get me an item.
list << *item; // put copy of item in list
delete item; // destroy my item
}
When expressed that way, it is obvious that we shouldn't use new at all!
for(int i = 0; i < 10; i++)
{
MyType item;
list << item;
}
This version won't leak, assuming that MyType doesn't have any memory-management bugs of its own.
EDIT: As an aside, had your program been:
QList<MyType*> f() // List of POINTERS
{
QList<MyType*> list;
for(int i = 0; i<10; i++ )
{
MyType* item = new MyType();
list << item; // Storing a POINTER
}
return list;
}
Then, yes, you would have had precisely the memory leak you expected. QList does not automatically provide delete on pointer types.
I don't see any point of using new in your code, as you're not storing the pointers in the list, rather copies of the object created with new, and you're not deleteing it. So yeah, there is memory-leak in the function itself.
Seeing that QList is not a list of pointers, I can say that you shouldn't use new in your code:
QList<MyType> f()
{
QList<MyType> list; //note : its not a list of MyType*
for(int i = 0; i<10; i++ )
{
MyType item; //automatic variable
list << item;
}
return list;
}
When a QList gets destroyed / goes out of scope, it destroys its content with it. In your case, the content is made of copies of your objects (built from the implicit copy-constructor), not the objects themselves. The memory will leak in each iteration of the for-loop since the original object created by new MyType() will lose its pointer, but will remain allocated.
There certainly will be a leak if it you don't delete all of those items you created with new MyType()!
In the destructor for QList you need to go through the list and call delete on each of those items.