🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

C++ Workshop - Pointers (Ch. 8)

Started by
87 comments, last by mihaimoldovan 12 years, 3 months ago
Strap in for a long one.

Ive been working on the project, even though its a week overdue. I just dont have alot of time to work on this. But, its written and compiles but with a lot of bugs.

The Crux is this, I create an object in a deep scope and then loose it once im out of that scope. I missed the part about objects going out of scope, but know int,char..etc variables go out of scope. Never clued in that int,char..etc are them selves special classes, so if they go then my defined class objects would go. Anyway, I went back and reviewed pointers again and "pointerized" my code, but something worries me. (that something comming in a few).

In my project I create an object in a deep scope, it needs to be avail in some outer scopes and then killed in an entirely different scope. I wrote two programs to simulate what I wanted to do and looked at thier behavior. The first code posted here uses new and creates everything on the heap/free store. There are two outer scope pointers used to reference whatever is created in the innerscope. Now, im left with a delema(still not what bothers me). I cant call delete on the objects outside the scope because they are ... well outside the scope --memory leak-- and my outer pointer will still look at the inner objects created data but that data isnt safe now that I have that the two inner pointer dangling, nor can I now delete them. Now, I added an if that deletes the inner pointers/objects in scope but now the outerpointers see trash.

In the second snipped of code here, I did exactly the same thing but stayed on the stack. Once the inner loop is done the destructor is called on the inner objects but the outer objects still point to them and can still see their data. But isnt this the stack equivalent of a stray pointer? I can see the data for now, but what keeps it from being written to by something else?

This is what is bugging me. Im missing a concept and its key to getting my project done in its current implimentation. Thanks for the time you spend on this.


first code where objects are on the heap/freestore
#include<iostream>#include<string>class test2;class test1{public: test1();~test1();   void set_val(int x);   int get_val();   void set_test2(test2 *ptest2);   test2 get_test2();private:	int t1val;	test2 *t2obj;};class test2{public:test2(); ~test2();  void set_val(int x);  int get_val();private:	int t2val;};using namespace std;test1::test1(){  cout<<"in test1 const"<<endl;} test1::~test1(){  cout<<"in test1 dest"<<endl;}void test1::set_val(int x){   t1val=x;}int test1::get_val(){  return t1val;}void test1::set_test2(test2 *ptest2){   t2obj=ptest2;}test2 test1::get_test2(){  return *t2obj;} test2::test2(){  cout<<"in test2 const"<<endl; } test2::~test2(){  cout<<"in test2 dest"<<endl;}void test2::set_val(int x){   t2val=x;}int test2::get_val(){   return t2val;}	int main()	{		test1 *t1obj=new test1; 		t1obj=NULL;		test2 *t2obj=new test2;		t2obj=NULL;		int outerloop=1;		int innerloop=1;		while (outerloop==1)		{		   cout<<"at outerloop start"<<endl;           int input;		   cout<<"enter 0 to stop program, or 1 to continue"<<endl;		   cin>>input;		   outerloop=input;		   while (innerloop==1)		   {			   cout<<"at innerloop start"<<endl;			    cout<<"enter a number"<<endl;		        cin>>input;		       test1 *t1=new test1;		       t1obj=t1;		       t1obj->set_val(input);			   cout<<"t1's val is "<<t1obj->get_val()<<endl;			   cout<<"enter a number"<<endl;			   cin>>input;			   test2 *t2=new test2;			   t2obj=t2;			   t2obj->set_val(input);               t1obj->set_test2(t2obj);			   cout<<"t2's val is "<<t2obj->get_val<<endl;();			   cout<<"enter 0 to stop innerloop, or 1 to stay"<<endl;                cin>>input;			   innerloop=input;              /* if (input==0)			   {			     delete t1; // <-- if this is done outside innerloop				 delete t2; // then it fails t1 and t2 are out of scope.. stray ptr			   }*/		   }// end innerloop		}//end outerloop		cout<<"t2's val is "<<t2obj->get_val()<<endl; //t1 and t2 are dead and t1/2obj		cout<<"t1's val is "<<t1obj->get_val()<<endl; // now point to trash				t2obj->~test2();		t2obj=NULL;		cout<<"t1's val is still "<<t1obj->get_val();		t1obj->~test1();		t1obj=NULL;		delete t1obj; // if t1/2obj dest not called		delete t2obj; // you get assertion failure				return 0;	}



second snipped of code where its all on the stack
#include<iostream>#include<string>class test2;class test1{public: test1();~test1();   void set_val(int x);   int get_val();   void set_test2(test2 *ptest2);   test2 get_test2();private:	int t1val;	test2 *t2obj;};class test2{public:test2(); ~test2();  void set_val(int x);  int get_val();private:	int t2val;};using namespace std;test1::test1(){  cout<<"in test1 const"<<endl;} test1::~test1(){  cout<<"in test1 dest"<<endl;}void test1::set_val(int x){   t1val=x;}int test1::get_val(){  return t1val;}void test1::set_test2(test2 *ptest2){   t2obj=ptest2;}test2 test1::get_test2(){  return *t2obj;} test2::test2(){  cout<<"in test2 const"<<endl; } test2::~test2(){  cout<<"in test2 dest"<<endl;}void test2::set_val(int x){   t2val=x;}int test2::get_val(){   return t2val;}	int main()	{		test1 *t1obj=NULL; 		test2 *t2obj=NULL;				int outerloop=1;		int innerloop=1;		while (outerloop==1)		{		   cout<<"at outerloop start"<<endl;           int input;		   cout<<"enter 0 to stop program, or 1 to continue"<<endl;		   cin>>input;		   outerloop=input;		   while (innerloop==1)		   {			   cout<<"at innerloop start"<<endl;			    cout<<"enter a number"<<endl;		        cin>>input;		       test1 t1;		       t1obj=&t1;		       t1obj->set_val(input);			   cout<<"t1's val is "<<t1obj->get_val()<<endl;			   cout<<"enter a number"<<endl;			   cin>>input;			   test2 t2;			   t2obj=&t2;			   t2obj->set_val(input);               t1obj->set_test2(t2obj);			   cout<<"t2's val is "<<t2obj->get_val();			   cout<<"enter 0 to stop innerloop, or 1 to stay"<<endl;                cin>>input;			   innerloop=input;               		   }// end innerloop		}//end outerloop		cout<<"t2's val is "<<t2obj->get_val()<<endl; //t1 and t2 are dead and t1/2obj		cout<<"t1's val is "<<t1obj->get_val()<<endl; // still point to tt1/t2 data, but is it safe?				t2obj->~test2();		t2obj=NULL;		cout<<"t1's val is still "<<t1obj->get_val();		t1obj->~test1();		t1obj=NULL;						return 0;	}
Advertisement
The second example has all the same problems as the first, plus pointers to destructed memory. You take pointers to an object, then the object is destroyed; that's bad.

The first example is almost correct.

Quote:
//Does test1 'own' t2obj? If yes, delete it here.//    and DON'T delete it anywhere elsetest1::~test1(){  cout<<"in test1 dest"<<endl;}//You are returning a copy of t2obj. Is that what you intended?//   return it by reference or pointer if not.test2 test1::get_test2(){  return *t2obj;}//inside main...//You declare pointers, allocate new objects... then immediatly//set the pointers to NULL, losing the objects you just created.//This is a memory leak. A really obvious one.		test1 *t1obj=new test1; 		t1obj=NULL;		test2 *t2obj=new test2;		t2obj=NULL;//You don't have to call the destructor explicitly like that.//just 'delete t1obj'. If test1 owns it's nested test2, don't//delete test2 here. Let test1 do it - in it's destructor.		t2obj->~test2();		t2obj=NULL;		t1obj->~test1();		t1obj=NULL;//This is just calling delete NULL. Which does absolutely nothing.		delete t1obj; // if t1/2obj dest not called		delete t2obj; // you get assertion failure
Lets see if i can clarify a bit... think of t2 objects as Items (armor and weapons) that are created and placed in an array. t1 is the character. t1 has reference to an Item object that he would have equiped and uses.

In a deep scope I want to create a character and have it available in outside scopes. If the character dies in a different scope then that character object is then deleted.

My approach was to define pointers in the top scope and initially null them. Then when the Character object is created in a deeper scope asign the outer pointers to those Character objects. If that Character dies, delete the Character and reset the corresponding outer pointer to null. But this has the now obvious problem of loosing the character once you leave the scope and making the corresponding outer pointer a stray. At no time do I want to delete the t2 (Item object), just kill its reference when the Character is destroyed.

Original post by Deyja
The second example has all the same problems as the first, plus pointers to destructed memory. You take pointers to an object, then the object is destroyed; that's bad.

The first example is almost correct.

Quote:
//You don't have to call the destructor explicitly like that.//just 'delete t1obj'. If test1 owns it's nested test2, don't//delete test2 here. Let test1 do it - in it's destructor.		t2obj->~test2();		t2obj=NULL;		t1obj->~test1();		t1obj=NULL;//This is just calling delete NULL. Which does absolutely nothing.		delete t1obj; // if t1/2obj dest not called		delete t2obj; // you get assertion failure


The problem was, if just called delete t1obj or delete t2obj the program would compile, but puke up an "assertion failure" when those lines were executed. If I called the destructor first it didnt spit that failure out, and being that in my project I would want to kill what ever t1obj or t2obj was pointing at but keep the pointers for the next created object, then I would want to set them to NULL so they are ready to be reused.

I think the problem here is a failure to understand the concept of 'scope'. When you create an object with new, it's not confined to a scope. I don't know how the book explains the concept of scope, nor how I can explain it better; but I can atleast rewrite your example such that it works. :)

Quote: The problem was, if just called delete t1obj or delete t2obj the program would compile, but puke up an "assertion failure" when those lines were executed.
That means you had a bug. You weren't working around it - you were hiding it, by leaking the memory instead. Your delete code worked because it wasn't actually deleting anything.

class Bar {};class Foo{public:   Bar* m_bar;};int main(){   Bar a_bar;   Foo* the_foo = 0;   while (true)   {       //recreate the_foo... over and over and over and over again...       delete the_foo;          //clean up the old foo       the_foo = new Foo();     //create the new foo       the_foo->m_bar = &a_bar; //set the Bar* inside the_foo to point at a_bar       int input = 0;       std::cin >> input;       if (!input) break;       //if input was 0, end the loop.   }   delete the_foo;              //clean up the leftover foo}
If condense my code as much as possible so you can see whats going on.

on lines 5/6 the main character pointers are created and set to null.

on lines 44/45 the character is created

on lines 60/61 character is deleted by choice

on lines 170/171 enemy character is created

on lines 202/203 character is deleted due to death, error
error C2065: 'playerCharacter' : undeclared identifier
error C2541: 'delete' : cannot delete objects that are not pointers

on lines 222/223 temp pointers are made to keep tabs on order

on lines 255/257 and 271/273 character is deleted due to death, error on both
error C2541: 'delete' : cannot delete objects that are not pointers

on lines 283/284 enemy character is delted no problems.


in this case do i set the pPlayer and pEnemy pointers to null before deleting thier referenced objects? and why is the compiler not recognizing playerCharacter as being defined and/or not a pointer?

//Create Menu and Item Objects up here//Wire up the Menu//declare a few outerscope variablesCharacter *pPlayer=NULL; //NULL;Character *pEnemy=NULL; //NULL;  while (loop==1)  //THIS ENTERS THE MAIN LOOP   {	          	     //checks for player existance	   		/////////////////////////////////////////////////  Menu Controll and Game Routing///////////////////////////////////////////////	  //sets current menu      //gets user selection for menu//////////////////////////////////////////////////////////////////  Display Choosen Menu//////////////////////////////////////////////////////////////		   		if (settype==1)//displays selected menu		      { }////////////////////////////////////////////////////////////////////////  Character Management Actions////////////////////////////////////////////////////////////////////          if (settype==2)  //character type specifics		  {			/ gets current menu action line		      if (charlinetype==2) // create char type                     {		               if (playerexists==true)                           {							   //informs you have a character                           }		       if (playerexists==false)		 	       {			         while (characterloop==1)//(5 scopes deep)				       {			             Character *playerCharacter=new Character;				         pPlayer=playerCharacter;                                  // Generates all character stats and vars				         					       while (switchloop==1)  							   {	//decision loop									switch (input)								{								  case 'Y': case 'y':									  {									// breaks out of the decision									  }								        							      case 'N': case 'n':									  {								     //kills character and recreates									 delete playerCharacter; //works here									 pPlayer=NULL;									 playerexists=false;									 switchloop=0;									 break;									  }									  								  default:									  {									 //bad choice									  }								}							}  //end switchloop while					}//end characterloop while				 }//end characterexists if			  }//end charactercreate line if              		      if (charlinetype==2)  //shows Character attributes			      {				   //checks for existance				   if (exist is false)                                     {				        // ask to create					  while (switchloop==1)  							{						         							 // decision loop																}  //end switchloop while				   }//end playerexists=false if			   }//end currentline=DispcharacterAttline if			 }//end settype=2 if///////////////////////////////////////////////////////////////  Purchase and Equip Items///////////////////////////////////////////////////////////		if (settype==3)//purchase/equip selected Item		   {			   			   if (itemtype==1) //weapon			   {				grabs weapon and checks for ownership					 if (owned==true)					 {					   //equips if owned					 }					 if (owned==false)					 {					    checks for purchase ability								if (buy==true)								{								   								   // purchase and asks for equip								   if (caseloop==true)								       {                                                                           // decision loop  								       }//end case if								}//end buy=true if 								else 								{								  // you dont have enough cash								}					 					 }// end owned=false if			   }//end type=1 if			                              if (itemtype==2) //armor			     {			        // all identical to //weapon above					 			      }//end Armor type if		   } // end if settype=3//////////////////////////////////////////////////////////////////////// This Starts/Controlls/and Ends a fight/////////////////////////////////////////////////////////////////////  if (settype==4)  // its in this scope we start to see the problem (just 1 deep from the main loop)        {          if (playerexists==false)	        {		     // checks for player		      while (switchloop==1)  		    	{						         			     //decision loop			    	}  //end switchloop while	         }//end playerexists=false if           if (playerexists==true)              {                     while (mainfightloop==1) // starts the fight sequence                 {                             //Grabs level you want to fight		           while (choiceloop==1)		            	{		                  // nags if you pick wrong level			            }// end choiceloop while		    						   /// Creating a New enemy ///(3scopes deep)		    Character *currentenemy=new Character;			pEnemy=currentenemy;		    // generate and set stats                    			   		       while (switchloop==1)  //chicken check decision loop		        	   {	                                      // chicken check if you want to fight					         		        		switch (input)						{	              	      case 'F': case 'f':		                     {                                        // lets fight, break out of chicken loop		                     }			             case 'R': case 'r':			                 {			             	     			             		 if (currentgold>=100)							 		 {						              //can pay gold to get out																              }								 else								 {						          // roll to see if you escaape						                 if (chickencheck<90)						                     {											//misses you live						                      }						                  else						                      {										       //you loose your head and dead							                    delete playerCharacter;									            pPlayer=NULL; //(5 scopes deep on a diff branch)							                   // kills character, resets menu and breaks main loop                                               // error playerCharacter is undefined (out of scope?)                                              // also told you cant delete something thats not a pointer                                              // spelling checked							                    }						           }//end else								}//end case r				        	 default:						    	 {								 // Nagg							      }					 } //end switch			   }//end chickencheck loop									  	          //Initiative check               // two temp pointers		      // three scopes deep                  Character *firstcharacter=NULL;		          Character *secondcharacter=NULL;	                           if (gofirst==1)	                {		            firstcharacter=playerCharacter;		            secondcharacter=currentenemy;	                }	              else 	                {		        	firstcharacter=currentenemy;		            secondcharacter=playerCharacter;	                }		//// Getting first character stats for the fight///		        //// Getting second character stats for the fight///	    	   	        while (innerfightloop==1) // repeating roll and text loop till death	                {			        	                    // lots of code to controll fight flow                    // breaks out when someone dies		          	                }// end innerfight loop;// determines reward or deletecs player and returns to main menu	if ((firstcharacterdeath==true) && (firsthuman==1)) // have to make sure hes human too	   {	       delete playerCharacter;		   pPlayer=NULL;		   firstcharacter=NULL;           // error cant delete something not a pointer           // three scopes deep diff branch           // breaks out to menu			  	   }	else	    {	      // sets gold and xp reward	    }	// end if firstcharacterdeath=true	if ((secondcharacterdeath==true) && (secondhuman==1))	  {	      Death_Msg();	      delete playerCharacter;		  pPlayer=NULL;		  secondcharacter=NULL;		   // identical to above with the same error	   }	else	  {	     xp gold reward	   }  // end if secondcharacterdeath=true     // cleans up the enemy, no errors     delete currentenemy;  //(2 scopes deep)     pEnemy=NULL;		// start check for level up	   }// end playerexists=true if} // end settype==4/////////////////////////////////////////////////////////////////////////  Breaks out of Main Game Loops and Allows Game to Exit//////////////////////////////////////////////////////////////////////          if (settype==5)//special type that affects loop		      {		        loop=0;		      }// end if settype=4              }//end main while loop ////////////////////////////////////////////////////////////////////////  Credits and Extras Section////////////////////////////////////////////////////////////////////   	return 0;}
Quote: delete playerCharacter;
pPlayer=NULL;


The name playerCharacter is from a different scope. You can't use that name. But that's fine; you already have a perfectly good name refering to that object: pPlayer. Just delete pPlayer.

Quote: in this case do i set the pPlayer and pEnemy pointers to null before deleting thier referenced objects?


No. You delete them using the pointer. You don't even need a seperate variable.
Quote: Original post by Deyja
Quote: delete playerCharacter;
pPlayer=NULL;


The name playerCharacter is from a different scope. You can't use that name. But that's fine; you already have a perfectly good name refering to that object: pPlayer. Just delete pPlayer.


So the object created with new is still there, just its name or symbol (right term?) isnt. Then that is a problem. I need pPlayer and pEnemy to be survivable and reassignable to the next playerCharacter and Enemy. If I delete the pointers then I have no way of recreating them in the outer scope without always writing over an active playerCharacter or causing the compiler to puke. Now in a different scope from where playerCharacter was created, its assigned a temp pointer and that pointer references the name playerCharacter.

**** How is it playerCharacter is valid in that scope(different branch and level) but not in the scope where I want to delete it?(also different branch and level) ****

edit-- The compiler did catch it after I removrd the referene to it where it first puked /shrug. Still in the same boat though.---

Originally my code compiled without pointers. But I lost reference to the object because of scope (calling the destructor). So new was the answer. I switched the code, now I have these 4 errors holding me up. If you would like to see all the code I can either post it here or zip it up. But at this point im stuck.

There has to be away to create an object deep in code and reference/destroy it in other areas of code. Its done with pointers, but you loose the pointer when you destroy it since you have to destroy the object by the pointer. The pointer would have to be defined in the begining of the code or you would loose it because of scope... so what am I missing here?

[Edited by - westond on September 20, 2006 4:12:18 PM]
Quote: How is it playerCharacter is valid in that scope(different branch and level) but not in the scope where I want to delete it?(also different branch and level)
playerCharacter is a pointer. It is NOT the object it points to. That object is somewhere else in memory, and other pointers can point to it too. You don't have to delete playerCharacter just because it goes out of scope: You delete the object it points to, using any pointer that points to it at all, when you are no longer using that object.

Quote: The pointer would have to be defined in the begining of the code or you would loose it because of scope... so what am I missing here?


Yes, the pointer has to be in an larger scope. Look again at my example from a few posts back. I declared the pointer in an outer scope - the function main - and then manipulated it in the inner scope - the while loop. Just because I declare the pointer outside the while does not mean I can not assign a new object to it inside the while, nor does it mean I must delete that object inside the while (Though for all but the very last delete, I do.) By being on the free store (I allocated it with new) the object is not constrained by scope rules. The pointer that points to it, however, is.

Read the chapter on pointers again. And don't feel frustrated; this is usually the hardest concept for new programmers to grok.
---edit---
Ok, figured out what concept I was missing. in the example below... I can do a delete ptr; all day long and continue to use ptr. I was under the impression that when I did delete ptr; I not only deleted the object, but the pointer aswell. My code is now up and running and its just a matter of cleaning up the logic abit, putting in some pauses here and there and then ill post it in the project forum









I think ive got the concept... sadly so. If you look at my code structure it has a serious flaw. If I simply do away with the pointers that reference my character object then my code can only be ran once, you can create a character... kill it but after that it will crash since the pointers that are made to hold the character objects no longer exit. At that there is no way to create a pointer in the larger scope so its available to every inner scope without either overwriting the pointer or creating a memory leak.

class foo{ public:a(){};b(){}"};main(){ foo *ptr=NULL;   while (some condition)    {      if (some other condition)       {         if (some other conditon)          {            foo *bar=new foo;            ptr=bar; // after 1 run, the program would break because ptr bar is no longer avail.          }         ptr->a();       }      if (condition that relates bar is alive)         {          ptr->b();         }         delete ptr;            }}


How do you create a program without being able to pass object to different scopes unless you know exactly how many times an object will be created and destroyed and then create a pointer before hand for each instance the object will have. Other wise you are limited to manipulating objects a set amount of times. The pointer is just a memory address on the stack that is the location of the object on the free store. Why cant you just call that memory address, free/delete it and then store null in that pointers memory location to be reused?

[Edited by - westond on September 20, 2006 11:12:59 PM]
Hi...

Sorry for posting this, I know this is out of place, but I needed an answer soon, so here goes:

How do I write to a file and read from it? I know a little about it but its the next section thats bothering me.

If I have a file that has some records in it, how do I search for a particular record, edit it and save the file and also to delete a particular record?

Any help would be great!

Thx

This topic is closed to new replies.

Advertisement