I have a question:
class B : public class A {
public:
vector<int*> vec;
};
class A {
};
vector<A*> vec_a;
vector<B*> vec_b;
if I push back an object of class B into both vectors.
B* b = new B;
vec_a.push_back(b);
vec_b.push_back(b);
then after that, I change something inside the object of class B,
such as:
int* i = ....
vec_b[0].push_back(i);
Does the vec_a change?
I am confused with that since I have checked that when vector push_back, it will only create a copy. But when I checked with the above codes, it changes. Are the two vectors hold a shared memory of object b?
Thanks
Yes, when pushing to a vector a copy is created. However in your case it's a copy of a pointer, not a copy of the actual object.
Your vectors contain pointers to a common object. Therefore anything you change inside that object via a derefence of one of those pointers is reflected in the object that they are pointing to. Adding the pointers to the vectors creates a copy of the pointers themselves, not the object they point to. Had you added a common instance of class B to both vectors, each vector would contain a separate copy of that object. Adding pointers results copies of your original pointer being added to each vector - but both copies will have the same value and that is the memory address of the object they point to.
You're putting a pointer to the same object in both vectors so any change to the object, pointed from an element in vec_a will affect the element in vec_b, too.
Yes. You create only one object, after all. You push pointers. So std::vector copy pointers, but you change memory they point to.
That's why you get your results. I suggest you read a bit more about pointers.
The vector push_back method indeed does create a copy. But since you have specified vector of pointers (to vec_a and vec_b), copying those pointer does not actually change the original object. You would get what you originally expected if you used
vector<A> vec_a;
vector<B> vec_b;
instead, filling those vectors with
B b;
vec_a.push_back(b);
vec_b.push_back(b);
would actually copy the content of b into the vectors. Changing one will not affect the other.
Related
If I use this line
std:vector<MyObject>* vec = new std::vector<MyObject>(100);
If I create MyObject's in the stack and add them to the vector, they remain in the stack, right ?
MyObject obj1;
vec->push_back(obj1);
So if it go to the stack, than MyObject's added to the vector will be gone after method ends ? What will I have inside the vector than? Garbage?
Should I use this instead ?:
std:vector<MyObject*>* vec = new std::vector<MyObject*>(100);
And if so, what about objects and primitives inside each MyObject ?
Should they also be dynamically created ?
Thank You
The std:vector as any other Standard Library container copies elements into itself, so it owns them. Thus, if you have a dynamically allocated std::vector the elements that you .push_back() will be copied into the memory managed by the std::vector, thus they will be copied onto the heap.
As a side note, in some cases std::vector may move elements if it is safe to do so, but the effect is the same - in the end, all the elements are under std::vector's jurisdiction.
A std::vector<MyObject> looks something like this (in reality it's much more complex):
struct Vector {
MyObject* data;
int size;
}
As you can see, the data is not directly inside the vector object. The vector always allocates the memory for the data on the heap. Here's what happens when:
you call .push_back: The vector copies the object into its own data block (which is on the heap and owned by the vector)
you copy the vector: The copied vector allocates new memory and copies all data from the existing vector into it
As you can see, the vector owns his data. That means, if you push_back a object into it, it doesn't matter where it came from because it gets copied.
If you have a std::vector<MyObject>* you have a pointer to a vector. This vector also owns his data, but you only have a pointer to it. That means, you have to delete it exactly once, otherwise you'll get a memory leak or a crash. Passing around pointers to a vector is OK, but need one class or function that "owns" it. This class/function has to guarantee that the vector still exists when the pointer to it is used.
The third case is a std::vector<MyObject*>. As every vector, this one also owns his data. But this time, the data is only a pointer. So the vector only owns the pointer, but not the objects to which the pointers are pointing. If you do something like this:
std::vector<MyObject*> getObjects() {
MyObject obj1("foo");
MyObject obj2("bar");
std::vector<MyObject*> vec;
vec.push_back(&obj1);
vec.push_back(&obj2);
return vec;
}
The returned vector only contains garbage because you only saved the address to a object on the stack to it. These objects are destroyed when the function returns, but the pointers in the vector are still pointing to that memory block on the stack.
Additionally, keep in mind that this
std:vector<MyObject*>* vec = new std::vector<MyObject*>(100);
Doesn't store heap allocated objects to the vector. It just stores pointers of type MyObject. If you want to do something like that remember to create the objects first, before you use them. You can do that with something like the following:
for (int i = 0; i < vec->size(); i++) {
vec->at(i) = new MyObject();
}
From my understanding so far, if you have a vector of class objects, if you erase any member of the vector, typically the vector will reallocate some of it's objects in order to preserve memory contiguousness. Hence you need to implement the rule of three (destructor, copy constructor and copy assignment operator) for everything to be preserved when erasing vector members.
However: for a vector of pointers to class objects the outcome is less clear to me.
If I erase a member, then surely C++ is smart enough to just copy the pointers around - not maddeningly delete the pointer (and the class object it points to) then re-create it and the object it points to again?
If this is not the case, can someone explain this idiocy to me?
The vector will delete, construct, and copy whatever type it contains. In the case of a vector of pointers to a class/structure, it will delete, construct, and copy pointers, leaving the actual objects the pointers point to alone. It is up to you to allocate and deallocate these.
EDIT
An example:
If you have the following:
class A
{
A() {}
}
void foo(void)
{
A * pointerToA = new A;
}
At the end of the function foo's scope the only thing that is deallocated is the memory for the variable pointerToA itself, i.e. 4 bytes that hold an address (in 32 bit) - which in this case is stored on the stack. The only way that the memory allocated for a new instance of class A will be freed is if you manually call delete with the address to pointerToA.
Let's take the example of an array of class A
A ** arrayOfPointerToA = new A*[10];
for(unsigned i = 0; i < 10; ++i)
arrayOfPointerToA[i] = new A;
which is similar to what happens when you have std::vector<A*>. When you call
delete [] arrayOfPointerToA;
you're deallocating the memory for the array of pointers, not for each A.
In the above diagram, the memory deallocated by the above call to delete is highlighted in red. Note that each A is stored at a random location in memory in this instance since they were all allocated separately.
Now taking this to a vector:
A std::vector<A> effectively uses new A[size] to allocate memory. If you're storing a raw pointer, this would mean it would allocate an array of type A, which means that size number of objects of type A are created. When the vector frees its memory size number of objects of type A are destroyed. Now take that example and replace A with A* and you'll see that no objects of type A are destroyed.
This is a fundamental part of how C++ and pointers work, not just a property of containers. If containers did arbitrarily call delete on each member, this wouldn't make sense as when we have a container A we would call delete on an instance of an object instead of a pointer to that object which is not valid.
The vector will leave your pointer values alone. It of course will move the values in the internal array when you push, pop, or erase.
In this case the values are just pointers. But there is no logic in the vector to determine if something is a pointer to an object and delete/reallocate them when the values are copied.
In the case of a vector that includes a complex type and not a pointer it will of course try to copy the values when the internal array is reallocated or moved.
Recently, I was confused on why I continuously was faced with a segmentation fault trying to access an element in a vector of pointers of certain objects. I didn't manage to resolve the issue, but I suspect that it is because after I pushed the object pointer into a vector, I called delete on it, thinking that the vector stored a copy.
In the following code:
std::vector<SomeObject *> testvector;
SomeObject * testobject = new SomeObject(/* some arguments here, call constructor */)
testvector.push_back(testobject);
delete testobject; // does this affect the element in the vector?
The debugger confirms that the pointers getting added to the vector do indeed have proper data, but once I call delete on them, I'm unsure if the element inside the vector is affected. Does the vector store just a copy? When I call delete on the object, I suspect that delete is being called on the pointer in the vector, but I am unsure.
Now I tried to print out the data in the vector after calling delete, I get this: ??? ?? ???? ???
And I am assuming that the call to delete has affected the vector element. Is this the case? I thought that after I added the pointer to the vector, I could safely free the object without affecting the element in the vector, but it seems that I end up accessing memory that is no longer allocated. Does the call to delete affect the pointer in the vector?
"Does the call to delete affect the pointer in the vector?"
It doesn't affect the pointer. It affects the behavior invoked by using this pointer since the object it points to no longer exists. When you call delete, the object is deleted and whatever you try to do with that object using invalid (old, dangling) pointer, the behavior is undefined.
std::vector<SomeObject *> testvector;
SomeObject * testobject = new SomeObject()
testvector.push_back(testobject);
Constructs a vector of pointers, creates an instace of SomeObject and pushes an address of this object to your vector. Then when you call:
delete testobject;
There is no way how std::vector could know that the object has been deleted. Your vector still contains an old pointer, which has became invalid by the time the object was deleted. A possible solution could be using a vector of smart pointers such as shared_ptr, however at first you should consider whether you want to use a vector of pointers at first place. Maybe std::vector<SomeObject> would be more reasonable way to go.
The vector contains pointers not the objects theirself. So after deleting an object the corresponding pointer will be invalid.
When you copy a pointer, you only copy the address of the object, not the object itself. That means the pointer in your vector and your pointer outside the vector were still referring to the same thing when you called delete.
The reason the debugger may still have shown seemingly valid data is because the memory doesn't necessarily get overwritten when you delete something. It's simply marked as 'free' so that it can be used by something else later if required.
Yes, both testobject and the inserted element into the vector are pointing to a same address. After deleting one of them another will be a dangling pointer and dereferencing it is undefined behavior.
You can use smart pointers such as std::unique_ptr or std::shared_ptr.
std::vector<std::shared_ptr<SomeObject>> testvector;
std::shared_ptr<SomeObject> testobject(new SomeObject);
testvector.push_back(testobject);
or
std::vector<std::unique_ptr<SomeObject>> testvector;
std::unique_ptr<int> testobject(new SomeObject);
testvector.push_back(std::move(testobject));
// After `std::move` you can not use `testobject` anymore!
Lets say I have an object that I dynamically allocated.
If I push it into a STL vector, will a reference be inserted into the vector or a copy of this object?
It's a general question. For example:
class vec {
vector<obj> vec;
void addToVec(obj a) {
// insert a into vec
}
...
...
...
}
obj* a = new obj;
vec* v = new vec;
vec.addToVec(a);
If I delete v, will object a die as well?
will a reference be inserted into the vector or a copy of this object?
Copy (which means that your class should be copy-able otherwise compiler error).
Clarification: References cannot be assigned in std::vector<>. Also, here object has broader sense, it can be a normal variable or a pointer, but std::vector<> accepts only copy.
Update: Post C++11, most of the standard containers offer std::move() of the object using "rvalue based API methods"; where a copy may not be performed.
If you have an object of type T that you have dynamically allocated and you push a pointer to the object onto a std::vector<T*>, then a copy of the pointer is pushed. If you dereference the pointer and push the result onto a std::vector<T>, then a copy of the object is made. Collections always make copies. So collections of pointers make copies of the pointer and collections of class instances make copies of the instances themselves (using copy construction IIRC).
Have you checked the reference:
void push_back ( const T& x );
Add element at the end
Adds a new element at the end of the vector, after its current last element. The content of this new element is initialized to a copy of x.
This effectively increases the vector size by one, which causes a reallocation of the internal allocated storage if the vector size was equal to the vector capacity before the call. Reallocations invalidate all previously obtained iterators, references and pointers
Consider the following public method that adds an integer variable to a vector of ints(private member) in a class in C++.
KoolMethod()
{
int x;
x = 10;
KoolList.Add(x);
}
Vector<int>KoolList;
But is this a valid addition to a vector ??? Upon calling the method, it creates a local variable. The scope of this local variable ends the moment the execution control leaves the method. And since this local variable is allocated on a stack(on the method call), any member of KoolList points to an invalid memory location in deallocated stack which may or may not contain the expected value of x. Is this an accurate description of above mechanism ??
Is there a need for creating an int in heap storage using "new" operator everytime a value needs to be added to the vector like described below ????:
KoolMethod()
{
int *x = new int();
*x = 10;
KoolList.Add(x);
}
Vector<int*>KoolList;
But is this a valid addition to a vector?
Yes, a (standard library) vector stores copies.
Is there a need for creating an int in heap storage using "new" operator
If you don't want the objects to be copied or to work with polymorphic objects (see object slicing) you'd use pointers. In that case you should preferably avoid dealing with deallocation manually and use wrappers (smart pointers or pointer containers) though to get exception safety back.
A Vector<int> (at least if it is std::vector) stores elements by value, so calls to add() creates a copy of the parameter object and stores that copy into the array. Therefore it doesn't matter what happens with the original object, the copy within the vector is alive as long as the vector itself (unless removed or overwritten explicitly, of course).
A Vector<X*> may be more appropriate if you
want to work with polymorphic X objects,
don't want to copy objects of X e.g. because it's expensive or disallowed,
want to share the same objects between different parties.
Of course, none of these applies to int, only to real objects. Still, it is better to store smart pointers in the vector instead of raw pointers, e.g. vector<auto_ptr<X> > or vector<shared_ptr<X> >. These automatically manage the disposal of objects for you.
If you create a vector of ints, it will not be a vector of pointers. The integers are stored by value, no pointers involved, and therefore you won't run into any problems with invalid memory addresses.
Here's an example of code that would cause such a problem:
std::vector<int *> my_list;
void a_method() {
int value = 2; // allocated on the stack
my_list.push_back(&value); // pushes a pointer to the stack... not good
}
Think about this: adding to the standard vector creates copy of added object.
In the first code snippet, you use vector of ints, so you'll add the copy of local int and everything is fine.
In the second code snippet you use vector of pointers to int, so you'll add the copy of a pointer (not the copy of the object this pointer is pointing to). Since the object pointed by the pointer will be still valid after leaving the method (it's initialized using new operator and it's not deleted anywhere) everything will be fine too.