If I create an object on the stack and push it into a list, then the object loses scope (outside of the for loop in the example below) will the object still exist in the list? If the list still holds the object, is that data now invalid/possibly corrupt?
Please let me know, and please explain the reasoning..
Thanks,
jbu
class SomeObject{
public:
AnotherObject x;
}
//And then...
void someMethod()
{
std::list<SomeObject> my_list;
for(int i = 0; i < SOME_NUMBER; i++)
{
SomeObject tmp;
my_list.push_back(tmp);
//after the for loop iteration, tmp loses scope
}
my_list.front(); //at this point will my_list be full of valid SomeObjects or will the SomeObjects no longer be valid, even if they still point to dirty data
}
EDIT: so what if it were a std::list<SomeObject*> my_list; instead of list...in that case would it be invalid?
The standard containers make a copy of the object so the list is still ok in your example.
All containers make a copy of what they store. It's a requirement that an object be copy-constructible and assignable, if it is to be used in a container.
So yes, vector, list, etc. all make a copy of your object.
An even shorter example:
struct foo {};
std::vector<foo> v;
v.push_back(foo());
// makes a copy of the temporary, which dies at the semicolon.
If it didn't make a copy, the above code would be bad.
The following code is not ok:
struct foo {};
std::vector<foo*> v;
{
foo f;
v.push_back(&f); // fine, but...
} // ...now f stops existing and...
v.front(); // ...points to a non-existent object.
Yes, it's valid. push_back makes a copy.
With all STL containers (lists, vectors, maps, everything), the containers make a copy of what you add to the containers so, so long as what you add isn't a pointer or reference, you're safe.
If you write your own containers though, you have to be careful how you do things, since there's nothing stopping you from writing a type of container that stores references -- it would just be a nasty surprise to anyone that thought it worked like a standard container.
Related
I would like to know whether it is ever necessary to move values explicitly when I am not inside a move constructor / move assignment. Lets say I have the following situation:
struct Node
{
std::vector<int> items;
};
Node create()
{
std::vector<int> items;
items.push_back(2);
Node n;
// should I use n.items = std::move(items);
// instead?
n.items = items;
// other code, not using items any more
return n;
}
First of all, is it correct to move an lvalue using std::move provided that I don't use the moved value any more in subsequent code?
Secondly, is it faster to move the value explicitly or does the compiler automatically detect that value can be moved?
You may avoid moving and leave it to initialization of an object and optimization of the compiler:
struct Node
{
std::vector<int> items;
};
Node create()
{
std::vector<int> items;
// ...
return Node{items};
}
The usual advice is: Do not apply overhasty manual optimizations.
Moving an lvalue is fine so long as you remember that lvalue might not be valid anymore. You can copy something over it and it'll be fine once again, though.
Moving the contents of a vector is generally faster than copying it as it only has to swap the underlying pointer (and a size variable) rather than individually copying each element in the vector. Moving something simple, like an int, is kind of silly, though.
Finally, in your example, it certainly wouldn't hurt to be explicit. items is an lvalue, so unless your compiler is clever enough to see items is never used again at all, it probably will use the copy semantics, so being explicit would help.
I have:
class Foo {
std::vector<Thing>things;
void bar();
}
I need to modify the Thing's stored in things in bar:
void bar(){
//How do I read a Thing stored in the vector, without making a copy?
Thing thing = things[0]; // Doesn't this make a copy ?
//.....
}
How do I read a Thing stored in the vector, without making a copy? If I do:
Thing &thing = things[0];
the members of thing are not initialized.
The second snippet is spot-on: the line below does not make a copy.
Thing &thing = things[0];
Something else is wrong with your code, probably a copy constructor. Since thing variable is a reference to the object inside the vector, if you see thing partially initialized, the object inside the vector is partially initialized as well.
To fix this problem, make sure that your code has a proper copy constructor, and follows the rule of three.
But why do I need a copy constructor if I am using a reference?
Since your vector contains objects, not pointers, copy constructor gets invoked when your object is inserted into vector<Thing>. If the copy constructor is required because your constructor allocates resources, but is not provided, your object may end up partially initialized when the destructor of the object that has been inserted into the vector is invoked.
You can use pointers.
Thing *thing = &things[0];
thing->pong();
If this is not initialized either, your vector is not ready.
You can use iterator:
vector<Thing>::iterator iter = things.begin();
//then use iter as a pointer;
*iter = Thing();
Suppose I have a struct with a member that is a vector. In the constructor, I want to just set it to NULL. But then later I want to be able to push_back things to it. How do I initialize the vector after it's NULL?
struct structName {
vector<int> vec;
structName() {
vec = NULL
}
void set(int);
}
void structName::set(int n) {
// What do I put here?
vec.push_back(n);
}
Thanks for the help!
It's already initialized via the default constructor and, if you need to call a different constructor, use an initialization list.
Remember, this is C++, not Java/C#/whatever. It makes no sense for an object to be null (ok, it doesn't in those languages either, but read on). It can't happen. In Java and languages like it you have variables which are references to objects and those references (not objects!) may or may not be null.
That is not the case in C++. There is a strict delineation between objects and pointers which refer to them (and pointers, or course, can be null or refer to an invalid memory location).
The default constructor for the vector will set it to its simplest state: no contents. So just leave it alone.
I think you be thinking of C++ objects like you would in Java. DON'T They are totally different beasts.
In C++, objects aren't references as in Java, thus, nulling them out doesn't make sense. In fact trying vec = NULL is an error as NULL is really just 0.
Instead just remove the line vec = NULL and it'll work as is. The vector will be default constructed as empty and you don't have to do any other initialization.
Initializing a vector to NULL doesn't make any sense. Perhaps you're getting confused with pointers. If you want to initialize your vector to the empty vector then that happens automatically. Like this in other words
struct structName {
vector<int> vec;
structName() {
}
void set(int);
}
void structName::set(int n) {
vec.push_back(n);
}
does the function set::insert saves a pointer to the element or a copy of it. meaning, can I do the following code, or I have to make sure that the pointers are not deleted?
int *a;
*a=new int(1);
set<int> _set;
_set.insert (*a);
delete a;
*a=new int(2);
_set.insert (*a);
delete a;
I gave the example with int, but my real program uses classes that I created.
All STL containers store a copy of the inserted data. Look here in section "Description" in the third paragraph: A Container (and std::set models a Container) owns its elements. And for more details look at the following footnote [1]. In particular for the std::set look here under the section "Type requirements". The Key must be Assignable.
Apart from that you can test this easily:
struct tester {
tester(int value) : value(value) { }
tester(const tester& t) : value(t.value) {
std::cout << "Copy construction!" << std::endl;
}
int value;
};
// In order to use tester with a set:
bool operator < (const tester& t, const tester& t2) {
return t.value < t2.value;
}
int main() {
tester t(2);
std::vector<tester> v;
v.push_back(t);
std::set<tester> s;
s.insert(t);
}
You'll always see Copy construction!.
If you really want to store something like a reference to an object you either can store pointers to these objects:
tester* t = new tester(10);
{
std::set<tester*> s;
s.insert(t);
// do something awesome with s
} // here s goes out of scope just as well the contained objects
// i.e. the *pointers* to tester objects. The referenced objects
// still exist and thus we must delete them at the end of the day:
delete t;
But in this case you have to take care of deleting the objects correctly and this is sometimes very difficult. For example exceptions can change the path of execution dramatically and you never reach the right delete.
Or you can use smart pointers like boost::shared_ptr:
{
std::set< boost::shared_ptr<tester> > s;
s.insert(boost::shared_ptr<tester>(new tester(20)));
// do something awesome with your set
} // here s goes out of scope and destructs all its contents,
// i.e. the smart_ptr<tester> objects. But this doesn't mean
// the referenced objects will be deleted.
Now the smart pointers takes care for you and delete their referenced objects at the right time. If you copied one of the inserted smart pointers and transfered it somewhere else the commonly referenced object won't be delete until the last smart pointer referencing this object goes out of scope.
Oh and by the way: Never use std::auto_ptrs as elements in the standard containers. Their strange copy semantics aren't compatible with the way the containers are storing and managing their data and how the standard algorithms are manipulating them. I'm sure there are many questions here on StackOverflow concerning this precarious issue.
std::set will copy the element you insert.
You are saving pointers into the set.
The object pointed at by the pointer is not copied.
Thus after calling delete the pointer in the set is invalid.
Note: You probably want to just save integers.
int a(1);
set<int> s;
s.insert(a); // pushes 1 into the set
s.insert(2); // pushes 2 into the set.
Couple of other notes:
Be careful with underscores at the beginning of identifier names.
Use smart pointers to hold pointers.
Ptr:
std::auto_ptr<int> a(new int(1));
set<int*> s;
s.insert(a.release());
// Note. Set now holds a RAW pointer that you should delete before the set goes away.
// Or convert into a boost::ptr_set<int> so it takes ownership of the pointer.
int *a;
*a=new int(1);
This code is wrong because you try to use the value stored at address a which is a garbage.
And, every stl containers copy elements unless you use move semantics with insert() and push_back() taking rvalue references in C++0x.
Let's say I have a class FOO.
I want to have a std::vector of FOO.
Is it better if I do something like this:
FOO foo;
foo.init();
foo.prop = 1;
std::vector<FOO> myvec;
myvec.push_back(foo);
foo.prop = 2;
myvect.push_back(foo);
or is it better practice to do:
std::vector<FOO> myvec;
FOO foo;
myvec.push_back(foo);
myvec.back().init();
myvec.back().prop = 1;
myvec.push_back(foo);
myvec.back().init();
myvec.back().prop = 2;
I'm basically not sure if its better to make a model and push in the model instead of making an instance, pushing it, then modifying it from the vector. Also, which one is safer and least likely to result in memory leaks?
Thanks
Best practice is not to have an init() function - you want a constructor. If you always need to set prop, give the constructor a parameter to do it. This has nothing specifically to do with vectors - it's the way that all C++ code should be written.
Neither method has any memory issues as you're dealing with values and aren't dynamically allocating any objects manually.
I would favour giving FOO a constructor which does whatever init does and sets prop to the appropriate value. Then you can just push the values you want:
myvec.push_back(FOO(1));
myvec.push_back(FOO(2));
I think the best is to:
myvec.push_back(FOO(1));
myvec.push_back(FOO(2));
The answer depends on what your FOO class does. If it's a simple structure with no pointers etc., then both your approaches are fine and do the same.
Note that push_back inserts a copy of the object into the vector. If your class allocates memory on the heap, you need a copy constructor that creates a deep copy of your objects, otherwise you'll end up with memory leaks. Also, if your objects are quite large, it may be inefficient to create copies. In such cases, what I generally do is allocate the object itself on the heap externally and insert the pointer into the vector:
std::vector<FOO *> myvec;
FOO *foo;
foo = new FOO();
foo->init();
foo->val = 1;
myvec.push_back(foo);
foo = new FOO();
foo->init();
foo->val = 2;
myvec.push_back(foo);
However, in this case, you need to remember to free the objects before destroying the vector.
Besides what others have said about initializing your objects in the constructor, I would add this:
In your second example, where you put objects in the vector and then initialize them, you risk leaving your vector in an unusable state.
If for example, the init() method can throw an exception, you'll have a non/partially initialized object in your vector.
Of course, these problems goes away with a constructor that makes sure the objects are properly initialized.
In general: Don't start doing stuff with objects before they're in a usable state.