Jump to content
  • Advertisement
Sign in to follow this  
asdqwe

smart vector - why?

This topic is 3677 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

#include <vector>
#include <iostream>
using namespace std;

void main()
{
	vector<int> vec;
	vec.push_back(33);//0
	vec.push_back(66);//1
	vec.pop_back();
	cout<< vec[1];
}
Output: 66. So how does it remember there used to be a value "66" in that place? I thought popback erases the last value and then any RANDOM value would take its place. But it appears the vector remembers:)...

Share this post


Link to post
Share on other sites
Advertisement
The reason is simple - undefined behaviour.

Undefined can include a simple crash, subtle memory corruption or even appearing to work correctly (and more). Or lovely combinations of the above, that change when you attach a debugger. Have fun [grin]

Share this post


Link to post
Share on other sites
That should cause a runtime error, it does when I try your code on my machine in VS2005. On the other hand, why would it put a random value in place of the old one? It just frees the memory as far as I know.

Share this post


Link to post
Share on other sites
It probably doesn't even free memory; when you 'pop_back' it could simply decrease a pointer to the 'last' element in the memory it has reserved. That way when you push something new in it doesn't resize and just overwrites what is there.

Share this post


Link to post
Share on other sites
@ Tim: Well this code
vector<int> a(3);
cout<<a[10];
spits a random number so I figured the 1st example would, too.

Share this post


Link to post
Share on other sites
Quote:
Original post by Tim Ingham-Dempster
That should cause a runtime error....

No, as rip-off mentioned, it's undefined behavior. It may cause a runtime error, but it can do anything include transform your computer into a beautiful lead crystal figurine. If you want an out of bounds exception, use at() rather than operator[].

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCranetransform your computer into a beautiful lead crystal figurine.


I hear about that a lot, but has it actually ever happened to anyone?

Share this post


Link to post
Share on other sites
Quote:
Original post by Rattenhirn

I hear about that a lot, but has it actually ever happened to anyone?


Don't ask....

Quote:
So how does it remember there used to be a value "66" in that place?


Because it never "forgot".

Although there's lots of talk about undefined behavior, it should be noted that in this particular case (considering actual implementation of std::vector, it isn't).

Vector is very conservative when it comes to releasing the memory. When it needs more, it usually allocates 2x the old size.

Now look at what can happen in this case (it's implementation dependant):

// X is undefined memory contents
Heap: [X][X][X][X][X][X][X][X][X][X][X]
vector<int> vec;

// Y is memory claimed by vec
Heap: [Y][X][X][X][X][X][X][X][X][X][X]

vec.push_back(33);//0
Heap: [33][X][X][X][X][X][X][X][X][X][X]

vec.push_back(66);
// oops, out of room, need to resize
// first, allocate 2x old size
// Y is allocated memory, but yet initialized
Heap: [33][Y][Y][X][X][X][X][X][X][X][X]
// next, copy old contents into new memory
Heap: [33][33][Y][X][X][X][X][X][X][X][X]
// release old memory
Heap: [X][33][Y][X][X][X][X][X][X][X][X]
// next, push 66
Heap: [X][33][66][X][X][X][X][X][X][X][X]

vec.pop_back();
// Vector hasn't re-allocated memory
// so the old heap contents are still valid
// and *defined*
Heap: [X][33][66][X][X][X][X][X][X][X][X]
0 1


Given actual implementation of std::vector, this is not undefined behavior from C++ perspective. vec[1] is valid memory location.

Conceptually, this is the same as saying:
int a, b, c;
bool allow_use_of_b;
if (allow_use_of_b) cout << b;
In above case, we may choose to say that b isn't valid, but from language perspective, b doesn't change, and remains valid.

At this point it should be pointed out that many a topics have been started about "ZOMG STL IS SLOOOW!!!!!". The first thing everyone does at that point is turn off checked iterators.

Duh!

The whole reason that performance hog exists is to check for this type of scenario. Checked iterators and secure SCL exist not to prevent undefined behavior, but to check for undesired and almost certainly invalid *defined* behavior.

Note that this type of problem can trivially occur even in managed languages. This is purely a logic error with regard to implementation of std::vector, where subscript operator does not check bounds - managed languages do (ZOMG C# IS SLOW, why doesn't CLR remove redundant bounds checks!!!!).

But it is not a buffer overrun, nor undefined behavior (sometimes, but in case of vector rarely, can be).

In managed languages, people often implement a list, but make a small mistake.
void List::remove(int i) {
size--;
contents = contents[size];
};
All fine and dandy. But what happens if List manages objects? contents[size] still references the object and remains alive. In managed languages, this would be a memory leak.

So from functional perspective, this type of behavior is closer to memory leak in managed languages than it is to buffer overrun.

See here (although article is mostly advertisement for a certain product, it was this type of problem).

The solution to above is obviously:
void List::remove(int i) {
size--;
contents = contents[size];
contents[size] = null;
};
In case of OP, there is no 'null' value that could be written, and leaving original value, or writing a random one wouldn't matter.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Although there's lots of talk about undefined behavior, it should be noted that in this particular case (considering actual implementation of std::vector, it isn't).


No, this is undefined. The operational semantics of v[n] on a vector v is *(v.begin() + n). If n is greater than or equal to the size of v, then you're dereferencing an iterator at end() or past end(). This is undefined behavior. See sections 23 and 24 in the C++ Standard, in particular section 23.1.1 paragraph 12.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!