Sign in to follow this  
vinb

What's the benefit of creating objects on the heap?

Recommended Posts

Well, as promised, here's the code for my black jack game. I had a question regarding using pointers to objects. I am using a pointer to the game ojbects (the house, player1, the deck) for no other reason than because it's how I saw somebody else do it. Now that I look at the code, it seems that I'm not really gaining anything because I could still pass the objects around to the functions by reference if I created the objects on the stack. Is there anything to be gained by creating the objects on the heap (especially in the context of what I'm trying to do here)? If you're as new to C++ as I am I hope you can get some benefit from the code ( even as an example of what NOT to do. :) ( Sorry for the double post, I mistakenly included this topi in response to another thread I had opened ) main.cpp
#include <iostream>
#include <iomanip>
#include <windows.h> 
#include "blackjack.h"
#include <ctime>
#include <time.h>

using namespace std ;

//FUNCTIONS
void updateScreen(const Player &player1, const Player &player2) ;
void dealCards(Deck *theDeck, Player *player1, Player *player2) ;
void hit(Deck *theDeck, Player *thePlayer) ;
void writeGame(const Player *theHouse, const Player *player1, int totalscore) ;
void writeHeadings() ;
void gameloop(Deck *theDeck, Player *theHouse, Player *player1, int &totalscore) ;
void resetDeck(Deck *theDeck) ;
COORD setPoint(const int x, const int y) ;
void SetConsolePosition(int x, int y) ;

const WIN = 1 ;
const LOSE = 0 ;
const LEFT_MARGIN = 20 ;

/////////////////////////////////////////////
// MAIN
/////////////////////////////////////////////
void main(){

	Deck *theDeck = new Deck() ;
	int totalscore = 0 ;
	char response ;

	do {

		Player *theHouse = new Player();
		Player *player1 = new Player() ;

		system("cls") ;

		theHouse->setScore('!') ;
		player1->setScore('!') ;

		gameloop(theDeck, theHouse, player1, totalscore) ;
		cout << "Play again? Y/N" ;
		cin >> response ;
		if (response == 'N' || response == 'n' ) break ;
		
		resetDeck(theDeck);

		delete theHouse ;
		delete player1 ;

		}while (true) ;


	delete theDeck ;
	
	system("pause") ;	
}

/////////////////////////////////////////////
void gameloop(Deck *theDeck, Player *theHouse, Player *player1,int &totalscore) {

	int cardCount = 0 ;

	dealCards(theDeck, theHouse, player1) ;
	cardCount = 2 ;

	// write to screen
	for (int i=0;i<2;i++){
		writeGame(theHouse, player1, totalscore) ;
		}

	char response ;
	bool hitTheHouse = true;
	do{

		if ( theHouse->getScore() == 21) {
			cout << "YOU LOOSE!!!" << endl ;
			totalscore -= 10 ;
			hitTheHouse = false ;
			break ;
			}

		if (player1->getScore() == 21){
			cout << "YOU WIN!!!" << endl ;
			totalscore += 10 ;
			hitTheHouse = false ;
			break ;
			}

		SetConsolePosition(0,3) ;
		cout << "Hit? (Y or N)" ;
		cin >> response;

		if (response == 'N' || response == 'n') break ;
		
		hit(theDeck, player1) ;
		
		writeGame(theHouse, player1, totalscore) ;
		cardCount++ ;
		
		if (player1->getScore() > 21){
			cout << "BUSTED!!!" << endl;
			totalscore -= 10 ;
			hitTheHouse = false ;
			break ;
			}
		if (player1->getScore() == 21){
			cout << "YOU WIN!!!" << endl ;
			totalscore += 10 ;
			hitTheHouse = false ;
			break ;
			}
	} while (true) ;

	// hit the house until player1 loses or the house busts.
	while (hitTheHouse){

		if ( theHouse->getScore() <= player1->getScore() ) {
			hit(theDeck, theHouse ) ;
			}

		writeGame(theHouse, player1, totalscore) ;
		cardCount++ ;

		if ( theHouse->getScore() > 21) {
			cout << "YOU WIN!!!" << endl ;
			totalscore += 10 ;
			break ;
			}

		if ( theHouse->getScore() == 21) {
			cout << "YOU LOOSE!!!" << endl ;
			totalscore -= 10 ;
			break ;
			}

		if ( theHouse->getScore() > player1->getScore() && theHouse->getScore() < 21 ){
			cout << "YOU LOOSE!!!" << endl ;
			totalscore -= 10 ;
			break ;
			}

		} ;

	}
/////////////////////////////////////////////
void SetConsolePosition(int x, int y){

	COORD point = setPoint(x, y) ; 
	HANDLE stdCon = GetStdHandle(STD_OUTPUT_HANDLE);

	SetConsoleCursorPosition(stdCon, point);

}

/////////////////////////////////////////////
void writeHeadings(){

	SetConsolePosition(LEFT_MARGIN,3) ;

	cout << setw(11) << "House:    "  << setw(11) << "You:      " << setw(11) << "Bank" << endl ;
	
	SetConsolePosition(LEFT_MARGIN,4) ;
	cout << "----------------------------------------" << endl ;
	}

/////////////////////////////////////////////
COORD setPoint(const int x, const int y){
	COORD point = {x,y} ;
	return point ;
}

/////////////////////////////////////////////
void writeGame(const Player *theHouse, const Player *player1, int totalscore){ ;

	system("cls") ;
	writeHeadings() ;

	cout << endl ;
	int houseTotal = 0, player1Total = 0 ;
	int colspace = 5;

	for (int i=0;i<10;i++){
		
		if(player1->hand[i].getName() == '!' && theHouse->hand[i].getName() == '!' ) break ;

		SetConsolePosition(LEFT_MARGIN,i+6) ;
		
		houseTotal += theHouse->hand[i].getVal() ;
				
		cout << setiosflags (ios_base::left) ;
		cout << theHouse->hand[i].getSuit() ;
		cout << setw(colspace) ;
		cout << setiosflags (ios_base::left);
		cout << theHouse->hand[i].getName() ;

		cout << setw(colspace) << setiosflags (ios_base::left);
		cout << houseTotal  ;
					
		player1Total += player1->hand[i].getVal() ;
		
		cout << setiosflags (ios_base::left);
		cout << player1->hand[i].getSuit()   ;
		cout << setw(colspace) ;
		cout << setiosflags (ios_base::left);
		cout << player1->hand[i].getName() ;
			
		cout << setw(colspace) << setiosflags (ios_base::left) ;
		cout << player1Total;
		cout << setiosflags (ios_base::left);

		cout << setw(colspace) << setiosflags (ios_base::left) ;
		cout << "$" << totalscore  ;
		
		cout << endl ;
		
	}
}

/////////////////////////////////////////////
void dealCards(Deck *theDeck, Player *theHouse, Player *player1){
	
	// start with 2 cards
	srand(time(NULL)) ;
	int index, i ;
	// deal the house
	for (i=0; i<2; i++){
		index = rand() % DECK_SIZE ;
		theHouse->hand[i] = theDeck->getCard(index);
		theHouse->setScore( theDeck->getCard(index).getVal() ) ;
		theDeck->getCard(index).Use() ;
	}

	// deal player 1
	for (i=0; i<2; i++){
		index = rand() % DECK_SIZE ;
		player1->hand[i] = theDeck->getCard(index);
		player1->setScore( theDeck->getCard(index).getVal() ) ;
		theDeck->getCard(index).Use() ;
	}


}
/////////////////////////////////////////////
void hit(Deck *theDeck, Player *thePlayer){
	
	int i=0, index ;
	do{
		if (thePlayer->hand[i].getName() == '!') break;
		i++;

	}while (true);
    do{
		index = rand() % DECK_SIZE ;
		if (!theDeck->getCard(index).Used() ){
			thePlayer->hand[i] = theDeck->getCard(index);
			thePlayer->setScore( theDeck->getCard(index).getVal() ) ;
			theDeck->getCard(index).Use() ;
			break ;
		}
	} while (theDeck->getCard(index).Used()) ;
}

/////////////////////////////////////////////
void resetDeck(Deck *theDeck){
	for (int i=0;i<DECK_SIZE;i++){
		theDeck->getCard(i).PutBack() ;
		}
	}
/////////////////////////////////////////////
int WinOrLose(Player *thePlayer){
	

	return 0 ;
}


blackjack.h
////////////////////////////////////////////
// CARD INTERFACE
////////////////////////////////////////////
const int DECK_SIZE = 52;
class Card{

private:
	char suit ;
	char name ;
	int value ;
	bool used ;

public:
	
	Card();
	~Card();

	char getSuit() const ;
	void setSuit(int i) ;
	char getName() const;
	void setName(int i) ;
	void setName(char c) ;
	int getVal() const;
	void setVal(int i);
	void Use() ;
	void PutBack() ;
	bool Used() ;
};

////////////////////////////////////////////
// DECK INTERFACE
////////////////////////////////////////////
class Deck{

private:
	int index ;
	Card card[DECK_SIZE] ;

public:
	Deck();
	~Deck();

	Card getCard(int card) ;
	void deal() ;
	int getIndex() ;
	
} ;

////////////////////////////////////////////
// PLAYER INTERFACE
////////////////////////////////////////////
class Player{
private:
	
	int score ;
	int status ;

public:

	Player();
	~Player();
	
	Card hand[10] ;
	int getStatus() ; 
    void stand() ;
	void setScore(int cardValue) ;
	void setScore(char cardValue) ;
	int getScore() const ;
	void setHand(Card card) ;
	//void doubleDown();

} ;



blackjack.cpp
#ifndef BLACKJACK
	#define BLACKJACK

#include "blackjack.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h> 

using namespace std;
 
////////////////////////////////////////////
// PLAYER 
////////////////////////////////////////////
int Player::getScore() const {

	return score ;
}

/////////////////////////////////////////////
void Player::setScore(char cardValue){
	if (cardValue == '!')
		score = 0 ;
	}

/////////////////////////////////////////////
void Player::setScore(int cardValue){
	score+=cardValue ;
	}

/////////////////////////////////////////////
int Player::getStatus(){

	return status ;
}

/////////////////////////////////////////////
void Player::stand(){

}
/////////////////////////////////////////////
Player::Player(){

	this->score = 0 ;
	this->status = 0 ;
	for(int i=0;i<10;i++)
		hand[i].setName('!') ;

}
/////////////////////////////////////////////
Player::~Player(){

}

/////////////////////////////////////////////
void Player::setHand(Card card){
	
}
////////////////////////////////////////////
// CARD
////////////////////////////////////////////
Card::Card(){
	value = 0 ;
	name = ' ' ;
	suit = ' ';
	used = false ;

}
/////////////////////////////////////////////
Card::~Card(){

}
/////////////////////////////////////////////
char Card::getName() const {

	return name ;
}
/////////////////////////////////////////////
void Card::setName(int i){
	
	if (i==0 || i==13 || i==26 || i==39) name = '2'  ; 
	if (i==1 || i==14 || i==27 || i==40) name = '3'  ; 
	if (i==2 || i==15 || i==28 || i==41) name = '4'  ; 
	if (i==3 || i==16 || i==29 || i==42) name = '5'  ; 
	if (i==4 || i==17 || i==30 || i==43) name = '6'  ; 
	if (i==5 || i==18 || i==31 || i==44) name = '7'  ; 
	if (i==6 || i==19 || i==32 || i==45) name = '8'  ; 
	if (i==7 || i==20 || i==33 || i==46) name = '9'  ; 
	if (i==8 || i==21 || i==34 || i==47) name = 'T'  ; 
	if (i==9 || i==22 || i==35 || i==48) name = 'J' ;
	if (i==10 || i==23 || i==36 || i==49) name = 'Q' ;
	if (i==11 || i==24 || i==37 || i==50) name = 'K' ;
	if (i==12 || i==25 || i==38 || i==51) name = 'A' ;
}

void Card::setName(char c) {
	name = c ;
}

/////////////////////////////////////////////
char Card::getSuit() const {
	return suit ;
}
/////////////////////////////////////////////
void Card::setSuit(int i){
	
	if (i >=0 && i < 15) this->suit = 'H' ; 
	if (i >=15 && i < 28) this->suit = 'D' ; 
	if (i >=28 && i < 41) this->suit = 'S' ; 
	if (i >=41) this->suit = 'C' ; 

}
/////////////////////////////////////////////
int Card::getVal() const{
	return value ;
}
/////////////////////////////////////////////
void Card::setVal(int i){
	if (i==0  || i==13 || i==26 || i==39) value = 2 ; 
	if (i==1  || i==14 || i==27 || i==40) value = 3 ; 
	if (i==2  || i==15 || i==28 || i==41) value = 4 ; 
	if (i==3  || i==16 || i==29 || i==42) value = 5 ; 
	if (i==4  || i==17 || i==30 || i==43) value = 6 ; 
	if (i==5  || i==18 || i==31 || i==44) value = 7 ; 
	if (i==6  || i==19 || i==32 || i==45) value = 8 ; 
	if (i==7  || i==20 || i==33 || i==46) value = 9 ; 
	if (i==8  || i==21 || i==34 || i==47) value = 10 ; 
	if (i==9  || i==22 || i==35 || i==48) value = 10 ;
	if (i==10 || i==23 || i==36 || i==49) value = 10 ;
	if (i==11 || i==24 || i==37 || i==50) value = 10 ;
	if (i==12 || i==25 || i==38 || i==51) value = 11 ;
}

//////////////////////////////////////////////
void Card::Use(){
	used = true ;
}

void Card::PutBack(){
	used = false ;
}

//////////////////////////////////////////////
bool Card::Used(){
	return (used)? true : false ;
}

////////////////////////////////////////////
// Deck
////////////////////////////////////////////
Deck::Deck(){

	for (int i=0;i<DECK_SIZE;i++){
		
        card[i].setVal(i) ; 
        card[i].setSuit(i) ;
        card[i].setName(i) ;
			
	} 
}
/////////////////////////////////////////////
Deck::~Deck(){
	
}
/////////////////////////////////////////////
void Deck::deal(){

}
/////////////////////////////////////////////
int Deck::getIndex(){
	return index ;
}
/////////////////////////////////////////////
Card Deck::getCard(int i){
	return this->card[i] ;
}
#endif

Share this post


Link to post
Share on other sites
The size of the stack is limited. Don't get me wrong, that doesn't mean you should be monitoring your stack usage at all times! However, allocating too many objects (or objects that are too big) on the stack can be dangerous, because when you overflow the stack, the program simply crashes.

In your case, you're using 52*8 + 4 = 420 bytes for your deck, and not much more for the player hands, which is still acceptable (average stack size is almost always over 1kb).

However, if you were using the stack for sprite objects, or for textures, or for storing large levels, you would soon run out of space.

Share this post


Link to post
Share on other sites
You also create objects on the heap when you don't know how many objects you need to store.

For instance, I'm building an asteroid clone. However, I never know at a certain time, how many objects I have. Allocating a large array of objects and hoping that large array does the job isn't really smart right?

So I have a list which contains all the objects. When the player pressed the FIRE button, I create a new CBullet() and push it into the list. This process is repeated all the time.

When an object is done with what it's suppose to do, it'll simple tell the object manager to destroy it by setting a little flag a la
this->SetState(DISPOSE);

Every 5 seconds the object manager finds the objects that are no longer needed(Remember the DISPOSE flag?), and removes them from the heap.

If I would be doing this on the stack, I would be limited in the number of objects and more important, I would lose alot of time finding the objects I need to render/update.

Toolmaker

Share this post


Link to post
Share on other sites
So if I understand correctly, if I'm uncertain of the number of objects that will be created ( or I'm certain there will be many ) then I should use pointers, otherwise just create the object on the stack. Besides the added complexity, are there any actual disadvantages using the method I did?

Share this post


Link to post
Share on other sites
you should really use the STL instead of making your own dynamically allocated arrays. that is the whole purpose of the STL in the first place. it allows you to have "smart arrays", which can expand / shrink as needed. this way, you dont have to manage the memory yourself and worry about memory leaks, plus, there is a wide variety of standard functions which allow you to operate on these containers. DONT BE AFRAID!!!! learning how to use a std::vector is fairly easy to learn... dont be scared by it, just google for some vector tutorials and learn how to use them, they are pretty strait forward to use.

as far as dynamically allocating memory yourself goes, i personally only use it in one situation. that is when i need to use polymorphism. you dont have to worry about this now, but when you learn about inheritense and polymorphism, you will probably start using dynamic memory and pointers a lot more. however, this doesnt mean you wont still be using the STL. instead of having a container of objects, you would have a container of pointers.

Share this post


Link to post
Share on other sites
the real reason you create object on the heap, instead of the stack is simple (and true for any structured language) ... if you need to the object / variable to exist after your function returns, it must be created on the heap (or in some cases, copied as a return on the stack, but that ONLY works for fixed size objects)

Think of it this way ... say your stack pointer is at 20,000 ... and you call a function: CreateWidget() which takes an integer telling it what type of widget to create ...

well, you probably do something like this ... push the return address (where you are now) onto the stack, and then push the int onto the stack (so now the stack is at 19,992) ... Then invoke the function ...

The function reads the top item of the stack as it's parameter, and decides to make the widget ... IF if created the widget on the stack (assume the widget is 20 bytes of data), then it would put the widget on top the stack ... (stack now at 19,972) ... but the process of returning to the caller involves UNWINDING the stack back to where it was prior to the function call ... (so when it unwinds the stack to somewhere between 19,992 and 20,000 (depending on if it uses C like or Pascal like calling conventions) ... the Widget is totally lost ... it was made, setup and stored ... but the calling program doesn't know where it is ... and even if it did know it was at 19,972 ... the next time the calling code calls another functions, it will once again overwrite those values in the stack ....

The HEAP to the rescue ... the heap is an area (or multiple areas) of memory that are NOT inside of the stacks address space ... so they will not be written to or messed with by stack operations ... they are basically just another area of memory that is allocated and deallocated as needed, using Operating System functions to manage them ... so this way you execute code like:

"Hey windows, gimme 20 bytes to use" ... and then you make your widget and write it to that 20 bytes ... then return the address of that 20 bytes to the caller ... (aka a pointer to the object on the heap ) ..

I know I didn't do the best possible job explaining .. but I hope this post adds a few thoughts you hadn't considered before.

Share this post


Link to post
Share on other sites
Quote:
Original post by Xai
I know I didn't do the best possible job explaining ..


Maybe said in another way, the main point of unmanaged heap allocation (a part from size of objects/arrays not known until run-time) is to remove the shackles of scope

Share this post


Link to post
Share on other sites
Thanks for the explanations everyone.
Quote:
Original post by graveyard filla
DONT BE AFRAID!!!!

The only thing we have to fear is fear itself as well as any references to fear itself AND any pointers to fear itself. I, myself, have a dangling pointer to fear itself from a past life.

Share this post


Link to post
Share on other sites
I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.
And when it has gone past I will turn the inner eye to see its path.
Where the fear has gone there will be nothing.
Only I will remain.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this