• Advertisement

Archived

This topic is now archived and is closed to further replies.

STL vector frustrations

This topic is 5003 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Is there any reason why pushing back an element into a vector causes the destructor of all elements to be called? I understand that when you pushback to a vector, the vector has to allocate more space, and move all the elements to the newly allocated space, but to call the destructor of every element in doing so seems absurd. Is there a way to stop this from happening?

Share this post


Link to post
Share on other sites
Advertisement
I don''t think thats correct if your using push_back but if it is the case you can use the "reserve" method to avoid reallocations

Share this post


Link to post
Share on other sites
Well, if the elements are moved to another location in memory, the previous location has to be freed, including any dynamic memory that any of the vector elements may have had. I look at it this way, if there is anything for a destructor to do it will do it, if an element/object within the vector has no need to do any cleanup, then then there is nothing to destroy and things go fine. Destructors are there because its necessary. I can''t really see any other way to put it.

Share this post


Link to post
Share on other sites
quote:
Original post by snk_kid
I don''t think thats correct if your using push_back but if it is the case you can use the "reserve" method to avoid reallocations


I did a very simple experiment to make sure that I''m not just crazy.


#include <iostream>
#include <vector>
#include <conio.h>

using namespace std;

class A {
public:
A() {}
~A() {
cout << "Why are you doing this to me?" << endl;
}
};

int main() {
vector<A> test;
A a;
const int repeats = 2;
for(int i = 0; i < repeats; i++) {
test.push_back(a);
}
getch();
return 0;
}


Before the getch(), I get my message printed out 3 times. When repeats = 5, I get my message printed out 15 times. Everytime I push back, with the exception of the initial push back, the message in A''s destructor is printed out.

This is frustrating me because the destructor in my class needs to free the memory it allocated when the class terminates.

Share this post


Link to post
Share on other sites
In other words... the following will crash


#include <iostream>
#include <vector>
#include <conio.h>

using namespace std;

class A {
public:
A() {
x = new int(0);
}
~A() {
delete x;
}
private:
int* x;
};

int main() {
vector<A> test;
A a;
const int repeats = 2;
for(int i = 0; i < repeats; i++) {
test.push_back(a);
}
getch();
return 0;
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You need a copy constructor and an assignment operator.

Share this post


Link to post
Share on other sites
quite quite simple really, the STL containers work on a copy prinicple, so when memory has to be reallocated and objects move they are infact copied via the copy constuctor.

In your example what you need to do is create a copy constuctor which does teh ''new'' and then copies the data from the old location so it can be free''d when the old copy is deallocated.

The other way around this is to store pointers or smart pointers to objects in your list, then the pointers will be copied around and objects wont be deleted/deallocated on resize.

Share this post


Link to post
Share on other sites
Take Anonymous Poster advice, make an apprioate copy and assignment operator. At the moment you have default copy constructor automaically created that just copies address.

The reason why its going to crash is because when you create a container of objects with your class setup like that.
In your previous example this is whats happening.

You create one instance, new is called once inside its constructor, then each time you push is back it makes a copy of the object thus copying the address. When the program exits, all the elements plus the first variable are destroyed calling delete 3 times on the same location and crashes.


[edited by - snk_kid on June 9, 2004 4:50:39 PM]

Share this post


Link to post
Share on other sites
Call test.reserve(repeats); to reserve memory and avoid reallocations. After a call to reserve(n), you can push_back() up to n total elements, and nothing will ever be reallocated, and destructors will never be called.

Share this post


Link to post
Share on other sites
another thing you can do to avoid the destructors being called is to store a vector of pointers to dynamically allocated objects. This requires less vector space and quicker resizes and copies, but you do have to remember to delete the objects before you clear the vector.

"That''s not a bug, it''s a feature!"
--me

Share this post


Link to post
Share on other sites
copy constructor and assignment operator and destructor

Rule Of Three

If you have one, you probably need them all cos you have resources that need careful management.

Share this post


Link to post
Share on other sites
The example I posted was really just meant as a simplified example to prove that the destructors are being called since snk_kid said they weren't, so I wanted to verify and confirm that they infact are. My real problem really has nothing to do with that simplified example.

My actual class does indeed have a copy constructor, assignment operator, and its own destructor. To go into further details about my problem, basically it's a smart pointer class I'm working with. When you declare the smart pointer to equal a real pointer, the smart pointer gets "ownership" over that pointer, meaning it will be responsible for deleting it when the smart pointer is destroyed, no other smart pointer will have the right to delete the contents. This ownership is nontransferrable and is only assigned when a smart pointer is given the value of an actual pointer.

So the copy constructor does not transfer ownership over to the smart pointer to copy into. So when I have a vector of smart pointers, and push onto the vector a new smart pointer, not only does the content of the smart point get deleted, but the ownership goes away.

The purpose of my smart pointer class is essentially to have multiple pointers pointing to the same object, but when the smart pointer with ownership finally deletes its contents, not only is it set to NULL, but all other smart pointers pointing to the same object are also set to NULL.

This way I can do something like this:



SmartPtr<int> x(new int(5)); // initial smart pointer, so it will have ownership over the int it allocates

SmartPtr<int> y; // some other smart pointer

y = x; // y will point to the contents of x, it will not have ownership over the contents though, so upon terminating, it will not delete the int that was allocated

x.release(); // x now deletes its contents, freeing memory and is set to NULL, additionally y is also set to NULL

if(y != NULL) {
cout << *y << endl;
} else {
cout << "Uh oh... y's contents have been deleted.\n" << endl;
}



Now to fix my problem temporarily I set my copy constructor to transfer ownership. This really isn't what I hoped for, which is why I asked if there's a way to push onto a vector without having all the destructors called and that doesn't make use of copy constructors but simply does a binary copy. I guess I could make my own vector class because quite frankly, I think a simply binary copy would really speed things up. The final alternative will be to take ChaosEngine's advice, and use pointers to my smart pointers, which sounds rather funny but oh well.

[edited by - Kranar on June 10, 2004 3:40:47 AM]

Share this post


Link to post
Share on other sites
Standard library containers have two requirements on the contained type: CopyConstructible and Assignable. In order to satisfy Assignable, given any two objects of the contained type T, t and u, then t = u must be valid and has the post-condition that t is equivalent to u. Since with your smart pointer class ownership is held by only the original object, logical equivalence fails. This makes your smart pointer unsuitible for use in any standard container.

Share this post


Link to post
Share on other sites
Have you considered using the boost library before? They have a package that provides 6 different smart pointers including embedded reference count, some of them have been made to work with STL containers. Its very robust library.

Share this post


Link to post
Share on other sites
The problem with your approach is that when x is released, y has no way of knowing that x was released. When you say you set the pointer to NULL, only x''s copy gets set to NULL, but when y tries to dereference the pointer, the memory it points to has already been freed so you get an access violation. You could potentially solve this by having the object that owns the pointer keep a list of all the other smart pointers that point to it but do not own it, but that would be tricky and quite wasteful in terms of memory and speed. The best solution that most people use is reference counting. Each object being pointed to keeps what''s called a reference count, which is the number of smart pointers that are currently pointing to it. When the reference count reaches zero, the object deletes itself (or has a global memory manager object delete it, or something similar). Whenever you call a smart pointer''s copy constructor or operator =, then you increment the object being pointed to''s reference count. Whenever you call operator = or when a smart pointer is destroyed (i.e. in the destructor), you decerement the old object''s reference count. Note that with this system, you cannot use pointers to primitive types (int, double, etc.) because they do not support reference counting themselves. You should make every object that a smart pointer can be pointed at a subclass of a reference-counting class. Reference counting also solves the problem of temporary copy objects, including when a vector needs to be resized and all of the elements need to be copied.

Share this post


Link to post
Share on other sites

  • Advertisement