Problem with std::vector of pointers - objects pointed to change arbitrarily.

Started by
5 comments, last by rip-off 15 years, 4 months ago
I've spent most of the morning trying to figure this out and conceding defeat. I'm fairly new to C++, pointers, and the library, but not programming itself. Quick Summary: I have a vector of pointers to class GameObject (a class i wrote). In a gameloop, I pass the vector by reference so each item in the vector can be rendered or have logic applied to it, etc. The problem is, the pointers in the vector seem to change arbitrarily after certain function calls (even when these function calls have nothing to do w/ the vector). So far I'm using directx and some windows coding. I had objects up on screen, moving around, handling input, until I tried to get the location of the mouse cursor via GetCursorPos. I'll try to just post relevant coding to demonstrate the problem and not drown it out with 100 pages of code. Loop.cc - just create/allocate some pointers to GameObjects and add them to the vector. Then, loop and handle Input/Logic/Render

#include <vector>
#include "game.h"
#include "Global.h"

using namespace std;

INPUTDATA InputData;

// the main loop
void MainLoop() {
	vector<GameObject*> gvec;
		
	// paramaters in GameObject are stuff like xpos, ypos, width...
	GameObject * g0 = new GameObject(200,300,64,64);
	GameObject * g1 = new GameObject(100,400,64,64);
	GameObject * g2 = new GameObject(400,400, 64, 64);

	// add each pointer to the vector.
	gvec.push_back(g0);
	gvec.push_back(g1);
	gvec.push_back(g2);

	while (HandleMessages()) {	   

		// get input data.
		Input(&InputData);

		// assertions added as quick way of verifying that the
		// pointer in gvec is still equal to the original pointer.
		// These three asserts don't fire (everything ok)....
		assert(g0 == gvec[0]);
		assert(g1 == gvec[1]);
		assert(g2 == gvec[2]);
		
		Logic(gvec, &InputData); // but somehow, in this function, the elements in the vector are altered.
		
		assert(g0 == gvec[0]);
		assert(g1 == gvec[1]);
		assert(g2 == gvec[2]); // <-- this assertion reliably fires, address of gvec[2] changes??? 

		Render(gvec);	   
	}	 
}




Here is the details of Logic function in Logic.cc. It may not appear to do anything right now because I've deleted all the code that works ok, but the the below still makes it crash.

void Logic(std::vector<class GameObject*>& gvec, INPUTDATA* InputData) {
	int x = 0;
	int y = 0;

	GetMouseXY(x, y); // If I comment this out or delete, everything works fine.	
}



Here is the def of GetMouseXY in WinMain.cc. This is windows stuff and it blows my mind that the vector is effected at all.

bool GetMouseXY(int& x, int& y) {
	LPPOINT p;

	if(GetCursorPos(p)) {
		x = p->x;
		y = p->y;
		return 1;
	} else {
		return 0;
	}
}



So far, this has been enough to make me want to call the vector class buggy (which seems very silly) and just roll my own version. I'm sure its far more likely I've done something wrong or there is something I'm missing. Any ideas?
Advertisement
There isn't a problem with std::vector<>, I can tell you that.

I think your problem is here:
bool GetMouseXY(int& x, int& y) {	LPPOINT p; // <-- uninitialised pointer	if(GetCursorPos(p)) {		x = p->x;		y = p->y;		return 1;	} else {		return 0;	}}

You probably should have:
bool GetMouseXY(int& x, int& y) {	POINT p;	if(GetCursorPos(&p)) {		x = p->x;		y = p->y;		return 1;	} else {		return 0;	}}

In the GetCursorPos() documentation, it says that the parameter is an "out" parameter. This means that the function is expecting to be able to write values to the pointed-at value. In your code, there is no such value.

Its a pretty common idiom in C program, to pass a pointer to a structure like that.
And how do you think the vector can be buggy if you even don't do anything with it between the asserts?

More probably you are doing something bad to memory somewhere and it manifests it in this way.
You are a Genius. :) :) :)

I swapped it and it seems to work fine.

Just another question though, why doesn't the program crash in the GetMouseXY part?? Why would the problem only appear later (in this case if I removed the asserts above, I finally got a segfault in the Render method)??
Quote:Original post by MooseHuffer
You are a Genius. :) :) :)

I swapped it and it seems to work fine.

Just another question though, why doesn't the program crash in the GetMouseXY part?? Why would the problem only appear later (in this case if I removed the asserts above, I finally got a segfault in the Render method)??

Because GetMouseXY has no way to validate that pointer - it just blindly writes to that memory location. Since the pointer was uninitialised, it could point to anywhere. If it didn't point to an invalid memory location, the function would just blindly overwrite what used to be at that memory location. This would likely cause a crash somewhere else down the line.
NextWar: The Quest for Earth available now for Windows Phone 7.
Also - No way am i saying vector is buggy, I know they bulletproof those things and the problem is almost 100% on some user that doesn't know what they are doing, thats just the perception I had.

I had lots more code in here, ran this thing to hell through gdb, and kept getting segfaults because elements 1 and 2 in the vector would point to strange memory places after calling getMouseXY. Its just removing that code before I post here makes it easier to digest.

Thanks for the help! It works great now and I've killed 3 hours on this so far. :/
Welcome to C++ [evil]

It doesn't perform any checks for you. Using an uninitialised pointer (among other things) is undefined behaviour. The best you can hope for is a crash. Next is obvious memory corruption.

Then you have the horror stories, subtle memory corruption bugs. Program behaviour changes when a debugger is attached or when run on a development computer as opposed to out in the wild, etc. Most experienced C++ programmers have been bitten by something similar.

Undefined behaviour is... undefined. If it crashed consistently then it would be well-defined behaviour [smile]. The best you can do is ramp your warning level up to max, and learn from your mistakes.

This is why I wouldn't recommend C++ for beginners. At least in more modern languages, you can deterministic compile or runtime error with similar code, because there may be no such thing as an "uninitialised" variable in such languages.

This topic is closed to new replies.

Advertisement