I have a class that contains a vector of pointers to objects of the same class.
class a {
public:
std::vector<a *> children;
};
With this variable declaration,
a *v = &a();
the following statements can be executed repeatedly without error.
(*v).children.push_back(&a()); //add a child to "v"
v = (*v).children[0]; //set "v" to the newly created child
If the above code is placed inside a for loop, it fails during the second iteration. Specifically, it fails to push back a new "a" pointer to children, so the children vector remains empty, and a "vector subscript out of range" error is returned. Below is a minimum working example.
#include <vector>
class a {
public:
std::vector<a *> children;
};
int main() {
a *v = &a();
(*v).children.push_back(&a());
v = (*v).children[0];
(*v).children.push_back(&a());
v = (*v).children[0];
for (int i = 0; i < 2; i++) {
(*v).children.push_back(&a());
v = (*v).children[0];
}
}
Irrespective of how many times the statements are repeated before the loop, the above program always fails during the second iteration of the loop. Is there something I am missing?
a *v = &a(); takes the address of a temporary value. NEVER DO THIS.
Instead, use new to allocate an a on the heap:
a *v = new a();
v->children.push_back(new a());
Note that you have lost any references to the original v as you keep reassigning it. This is a memory leak.
Related
I have a vector of instances of a class called Node. I want to be able to condition on whether or not specific indices of the vector are populated or not.
See the example code below:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Node {
int testVal;
public:
//Default Constructor
Node() {};
Node(int a){testVal = a;}
int getTestVal(){return testVal;}
};
int main(){
vector<Node> testVector;
testVector.resize(2);
Node testNode = Node(5);
testVector[1] = testNode;
for (int i = 0;i < 2;i++){
if (testVector[i] == NULL){
cout << "Missing Data" << endl;
}
else{
cout << testVector[i].getTestVal << endl;
}
}
}
The code crashes at the if statement. What is a good way to condition on if a specific index is empty?
What you are asking is not possible.
The vector stores values not pointers, and so you will never get null.
If you want to check for "empty" spots, then declare a vector that stores the address of the nodes:
std::vector<std::shared_ptr<Node>> testVector;
And to store an item in the second index of the vector you do:
testVector[1] = std::make_shared<Node>(5);
Now the rest of your code should work as expected (just need to fix that call to getTestVal() function).
I think you misunderstand C++ semantics.
std::vector< Node> testVector; // creates empty vector of Node objects, no Node allocations made here
testVector.resize( 2 ); // calls default constructor and instantiates 2 new Node objects here
// could be done as std::vector< Node > testVector( 2 );
That vector has already allocated memory for those 2 Nodes it exists as the default constructor defines the class. It sounds like you want something more like this:
...
std::vector< Node * > testVector( 2, null_ptr );
testVector[ 1 ] = new Node( 5 );
for( const auto & ptr : testVector )
if( ptr )
std::cout << ptr->getTestVal() << std::endl;
...
delete testVector[ 1 ];
As someone else mentioned, smart pointer objects can also be used to manage memory for you and behave similarly.
Supposing that I have a class called TextEntry with some instance variables and a method ask() (which returns an integer).
I then create a vector of type: std::vector<TextEntry*> d_text (a vector of pointers of type TextEntry). I wish to iterate over all the elements in this vector and call the method ask() applied to each element. Is this the correct way to do it?
for (std::vector<TextEntry*>::iterator it = d_text.begin(); it != d_text.end(); ++it) {
TextEntry* TextEntryPointer = *it; // first dereference the iterator
TextEntry TextEntry = *TextEntryPointer; // dereference the TextEntry pointer
int j = TextEntry.ask();
}
For whatever reason this implementation is giving me an error so it would be helpful to know if the reason why is because of the above code or some other problem in the rest of my project.
I wish to iterate over all the elements in this vector and call the method ask() applied to each element. Is this the correct way to do it?
Almost.
Your program doesn't call ask on any element pointed by d_text but on a copy of each pointed element.
I am confused why it is a copy.
You create a variable of type TextEntry. You copy-initialize it from *TextEntryPointer. That is why there is a copy.
Is there a way to call it on the original element?
Yes. Instead of copy-constructing a TextEntry variable, you could use a reference variable instead:
TextEntry& TextEntry = *TextEntryPointer;
This refers to the object pointed by the element in the vector.
Or more simply, don't create an intermediate variable at all:
int j = TextEntryPointer->ask();
Perhaps not even for the pointer:
int j = (*it)->ask();
You could simplify the loop as well:
for(TextEntry* ptr : d_text) {
int j = ptr->ask();
}
For whatever reason this implementation is giving me an error
There is a missing semicolon
TextEntry TextEntry = *TextEntryPointer // dereference the TextEntry pointer
PS. Avoid naming a variable with a same identifier as a type.
An example program with little bit assumption.
Look into main function for invoking the member function.
#include <iostream>
#include <vector>
using namespace std;
class TextEntry
{
public:
int ask()
{
return 1;
}
};
int main()
{
vector<TextEntry*> te;
TextEntry* te1 = new TextEntry();
TextEntry* te2 = new TextEntry();
TextEntry* te3 = new TextEntry();
TextEntry* te4 = new TextEntry();
te.push_back(te1);
te.push_back(te2);
te.push_back(te3);
te.push_back(te4);
for (std::vector<TextEntry*>::iterator it = te.begin(); it != te.end(); ++it)
{
TextEntry *t = *it;
cout<<t->ask();
}
}
Other answers are correct. But we have 2016 now, so we have c++11:
for (auto& text_entry_ptr: d_text)
{
int j = text_entry_ptr->ask();
}
Much simpler and cleaner.
I have a simple algorithm which returns a list of lists, where each inner list contains the nodes on a different level of a binary tree. I'm having trouble understanding how to "reset" the scope of my inner list (e.g. see below).
My tree is a simple toy tree like so:
struct Node {
int data;
Node *left, *right;
}
I use a simple bfs that should return a list of lists. I try to create a new list on each of the loops, but I'm not sure how to "clear" the list and start a new one.
std::vector< std::vector<Node *> > build_lists(Node *root) {
std::vector< std::vector<Node *> > result;
Node *newline = new Node { std::numeric_limits<int>::min(), nullptr, nullptr };
std::deque<int> q;
q.push_back(root);
q.push_back(newline);
Node *tmp;
std::vector<Node *> inner; // HERE IS WHERE IS SET THE FIRST INNER VECTOR
while(!q.empty()) {
tmp = q.front();
q.pop_front();
if (tmp == newline) {
result.push_back(inner);
std::vector<Node *> inner; // HERE IS WHERE I TRY TO ''RESET'' THE VECTOR
if (!q.empty())
q.push_back(newline);
} else {
inner.push_back(tmp);
if (tmp->left)
q.push_back(tmp->left);
if (tmp->right)
q.push_back(tmp->right);
}
}
}
Clearly, I have failed to understand scope and some basic language features. If anyone could help point me in the right direction, I would appreciate it.
You can't reset a variable by declaring it again, which is what your code is doing. You now have a second variable with the same name, that for the duration of the second variable that name points to the second variable.
Instead you need to use a method to clear the first variable.
vector does have a method to reset it's contents - vector::clear.
You should do this instead:
result.push_back(inner);
// std::vector<Node *> inner;
inner.clear();
If you need to clear something you've pushed inside a vector, you can do this:
vector< vector< int > > vvi;
vector< int > vi;
vi.push_back(1); // fill
vii.push_back(vi); // vii has a copy of vi as it is now.
auto & _v = vii[0]; // fetch a reference to the copy
_v.clear(); // clear it
vii[0].clear(); // same in one step
assert( vii[0].size() == 0 ); // still contains a vector at index 0 but it's empty
Notwithstanding the fact that you would be clearing vectors of pointers - as others have pointed out (ha) you need to be very careful not to 'lose track' of pointers.
Eg, this would be bad:
vector< int* > x;
x.push_back( new int(4) );
x.clear(); // leak
In order of an object to go out of scope, it has to be created INSIDE the loop, like this for example:
while(1) {
std::vector<int> v;
...
// at that point 'v' will go out of scope and thus destroyed
}
However, when you do this:
std::vector<int> v;
while(1) {
...
// 'v' is always the same at this point
}
You could use std::vector::clear() to reset your vector, but be careful, since you are using pointers. Unless you keeping track of the pointed objects, just clearing the vector would result to dangling pointers, thus Undefined Behavior and you don't want that to happen.
If you need to free the memory the pointers point to, then you should first delete the objects the pointers point to and then clear the vector.
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.
#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.