Sign in to follow this  

Pointer problem

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

Ok so here's my header file:

// This is the header file for the Classes
#include <string>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;

srand(time(NULL));           // Doesn't work, where do I place it?

const string basenames[10] = { "Troll", "Ogre", "Ooze", "Gargoyle", "Dragon", "Orc", "Elemental", "Blood-Elf", "Goblin", "Gremlin" };

void createCharacter();

class Character
{
private:
	string name;
	int health;
	int power;
	int defense;
	
public:
	Character();
	Character( string chname, int hp, int ap, int def );
	void setName( string chname );
	void setHealth( int hp );
	void setPower( int ap );
	void setDefense( int def );
	string getName();
	int getHealth();
	int getPower();
	int getDefense();

};

Character::Character() : name( basenames[ rand() % 10 ] ), health( rand() % 11 + 10 ), power( rand() % 11 + 10 ), defense( rand() % 11 + 10 )
{}

Character::Character( string chname, int hp, int ap, int def ) : health( hp ), power( ap ), defense( def )
{}

void Character::setName( string chname )
{
	name = chname;
}

void Character::setHealth( int hp )
{
	health = hp;
}

void Character::setPower( int ap )
{
	power = ap;
}

void Character::setDefense( int def )
{
	defense = def;
}

string Character::getName()
{
	return ( name );
}

int Character::getHealth()
{
	return ( health );
}

int Character::getPower()
{
	return ( power );
}

int Character::getDefense()
{
	return ( defense );
}


class ChDatabase
{
private:
	vector<Character> database;

public:
	ChDatabase( int slots );
	void addCharacter( Character thisCharacter );
	void removeCharacter( Character thisCharacter );
	void displayPlayer();
	void displayAllEnemies();

};

ChDatabase::ChDatabase( int slots )
{
	database.reserve( slots );
}

void ChDatabase::addCharacter( Character thisCharacter )
{
	database.push_back( thisCharacter );
}

void ChDatabase::removeCharacter( Character thisCharacter )
{
	
}

void ChDatabase::displayPlayer()
{
	//if( database.begin() == 0 )
		//cout << "No Player Exists." << endl;
	//else
	//{
		cout << "   Name: " << database[0].getName() << endl;
		cout << " Health: " << database[0].getHealth() << endl;
		cout << " Attack: " << database[0].getPower() << endl;
		cout << "Defense: " << database[0].getDefense() << endl << endl;
	//}
}

void ChDatabase::displayAllEnemies()
{
	if( (database.begin() + 1) == database.end() )
		cout << "Database empty." << endl;
	else
	{
		for( vector<Character>::iterator iter = (database.begin() + 1); iter != database.end(); ++iter )
		{
			cout << "   Name: " << iter -> getName() << endl;
			cout << " Health: " << iter -> getHealth() << endl;
			cout << " Attack: " << iter -> getPower() << endl;
			cout << "Defense: " << iter -> getDefense() << endl << endl;
		}
	}

}



And here's my source file:

#include "classheader.h"

int main()
{
	int choice;

	cout << "\tCreate-a-Character!\n\n";
	cout << "What would you like to do?\n";
	cout << "1 - Create your Character!\n"
		 << "2 - Generate some enemies!\n"
		 << "3 - Exit\n";
	cin >> choice;

	switch( choice )
	{
	case 1:
		createCharacter();
		break;
	case 2:
		//generateCharacter();
		break;
	case 3:
		break;
	default:
		break;
	}

return 0;
}

void createCharacter()
{
	string chname;
	char keep;

	ChDatabase(1);											// reserve 1 slot in vector to hold the player character

	do
	{

	system("CLS");

	Character* pplayer = new Character;  // Problem here

	*pplayer = player;                   // Problem here 

	cout << "\tWelcome to Character Creation!" << endl;
	cout << "Here are your stats: " << endl;
	cout << " Health: " << player.getHealth() << endl;
	cout << " Attack: " << player.getPower() << endl;
	cout << "Defense: " << player.getDefense() << endl << endl;
	cout << "Would you like to keep these (y/n)? ";
	cin >> keep;

		if( tolower( keep ) == 'n'  )
		{
			delete *pplayer;
			delete pplayer;		
		}
	}
	while( tolower( keep ) == 'n' );

	cout << "\n\nCharacter name: ";
	cin >> player.setName( chname );

	addCharacter( player );

	system("CLS");

	cout << "\tCongratulations! Here is your character: " << endl << endl;
	displayPlayer();

}



Now my problem lies within the createCharacter() function. Here's what I want to do. 1. createCharacter() function is called. Enter do-while loop. 2. do loop entered, allocate memory on the heap for a Character object 3. Create an object called player of the class Character, and assign this object to the memory allocated in step 1. I want the default constructor called when I create player 4. Now I want to use pointers in a way that I can use player.getHealth instead of player -> getHealth(). 5. If the user decides they don't want to keep this object, delete the object player, and go back to top of loop and start at step 1 again. Once the user decides they want to keep the object player, I want to exit the do-while loop, and then save that object player into my vector that is in ChDatabase. My pointers aren't correct and I'm a bit unsure of my delete's. Also are my addCharacter( player ) and displayPlayer() calls correct? Do I need to declare their class, ChDatabase? Also, where do I place my srand() to work properly? It won't even compile where it is now. Thank you!!

Share this post


Link to post
Share on other sites
I can start off with a few quick tips.

First of all, call srand once at the beginning of the main function.

You are using a variable called player, but I can't see that you have declared that anywhere...

At the beginning of your do/while loop:

...
Character* pplayer = new Character;

// create a reference to the new heap object
Character& player = *pplayer; // so we can use . instead of -> (Not sure why you want this though)

cout << "\tWelcome to Character Creation!" << endl;
cout << "Here are your stats: " << endl;
cout << " Health: " << player.getHealth() << endl;
...



If I were to check if a vector was not empty I would either:

if(database.size > 0)
...


or:
if(!database.empty())
...


Share this post


Link to post
Share on other sites
Ok I changed it, here's what I got:



do
{

system("CLS");

Character* pplayer = new Character;

Character& player = *pplayer;

cout << "\tWelcome to Character Creation!" << endl;
cout << "Here are your stats: " << endl;
cout << " Health: " << player.getHealth() << endl;
cout << " Attack: " << player.getPower() << endl;
cout << "Defense: " << player.getDefense() << endl << endl;
cout << "Would you like to keep these (y/n)? ";
cin >> keep;

if( tolower( keep ) == 'n' )
{
delete *pplayer;
delete pplayer;
}
}
while( tolower( keep ) == 'n' );

cout << "\n\nCharacter name: ";
cin >> chname;
player.setName( chname );

71) ChDatabase.addCharacter( player );

system("CLS");

cout << "\tCongratulations! Here is your character: " << endl << endl;
76) ChDatabase.displayPlayer();

}





And here's my compile errors



Compiling...
main.cpp
.\main.cpp(7) : warning C4244: 'argument' : conversion from 'time_t' to 'unsigned int', possible loss of data
.\main.cpp(61) : error C2440: 'delete' : cannot convert from 'Character' to 'void *'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
.\main.cpp(69) : error C2065: 'player' : undeclared identifier
.\main.cpp(69) : error C2228: left of '.setName' must have class/struct/union
type is ''unknown-type''
.\main.cpp(71) : error C2143: syntax error : missing ';' before '.'
.\main.cpp(71) : error C2143: syntax error : missing ';' before '.'
.\main.cpp(76) : error C2143: syntax error : missing ';' before '.'
.\main.cpp(76) : error C2143: syntax error : missing ';' before '.'
Build log was saved at "file://c:\Documents and Settings\Nate\My Documents\Visual Studio 2005\Projects\Aggregate\Aggregate\Debug\BuildLog.htm"
Aggregate - 7 error(s), 1 warning(s)





How do I correct these?

Share this post


Link to post
Share on other sites
Remove the expression
delete *pplayer;

The reference player is created within the do/while loop, so it is automatically destructed when this loop ends.
You can fix it like this:

...
if( tolower( keep ) == 'n' )
{
delete pplayer;
}
else
{
// The following lines has been moved into the do/while loop where the player reference still exists
cout << "\n\nCharacter name: ";
cin >> chname;
player.setName( chname );

ChDatabase.addCharacter( player );
}
...







I also suggest that you declare your database like this
vector<Character*> database;
And the addCharacter function:
void ChDatabase::addCharacter( Character* thisCharacter )
and skip using the player reference all together.

That should get you a step closer to a running version

Share this post


Link to post
Share on other sites
Quote:

I also suggest that you declare your database like this
vector<Character*> database;
And the addCharacter function:
void ChDatabase::addCharacter( Character* thisCharacter )
and skip using the player reference all together.


But then every time I added a character to the vector I'd have to create a pointer for it and pass the pointer to the funtion, wouldn't I?

If that's the case, I don't think I want to do that. The only reason I'm using pointers right now are so I can create an object, show it the user, if they like it, then keep it, if they don't like it, delete the object and create a new one to show the user.

ALSO, what's wrong with my ChDatabase.addCharacter( player ) and ChDatabase.displayPlayer() calls? Why am I getting errors for those?

AND

else
{
// The following lines has been moved into the do/while loop where the player reference still exists
cout << "\n\nCharacter name: ";
cin >> chname;
player.setName( chname );

ChDatabase.addCharacter( player );
}
...



I want to be able to access the player reference outside of the do-while loop, so I wouldn't want to move those lines into it. Couldn't I allocate the memory outside of the do-while, then declare the player inside the do-while, then outside of the do-while wouldn't I be able to access player via the pointer/memory allocation that I made outside of the do-while?

Share this post


Link to post
Share on other sites

You still have a few errors in your code so I made a version that should compile.
There is still things to fix/change but this is what I got with a minimum of hazzle. Feel free to ask if there is anything. A lot of guys here that can give you some useful tips on code design and code organization =)

Take a close look at the changes and the comments I have added.
I also threw everything in a single file for convenience.


// This is the header file for the Classes
#include <string>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;

const string basenames[10] = { "Troll", "Ogre", "Ooze", "Gargoyle", "Dragon", "Orc", "Elemental", "Blood-Elf", "Goblin", "Gremlin" };

void createCharacter();

class Character
{
private:
string name;
int health;
int power;
int defense;

public:
Character();
Character( string chname, int hp, int ap, int def );
void setName( string chname );
void setHealth( int hp );
void setPower( int ap );
void setDefense( int def );
string getName();
int getHealth();
int getPower();
int getDefense();

};

Character::Character() : name( basenames[ rand() % 10 ] ), health( rand() % 11 + 10 ), power( rand() % 11 + 10 ), defense( rand() % 11 + 10 )
{}

Character::Character( string chname, int hp, int ap, int def ) : health( hp ), power( ap ), defense( def )
{}

void Character::setName( string chname )
{
name = chname;
}

void Character::setHealth( int hp )
{
health = hp;
}

void Character::setPower( int ap )
{
power = ap;
}

void Character::setDefense( int def )
{
defense = def;
}

string Character::getName()
{
return ( name );
}

int Character::getHealth()
{
return ( health );
}

int Character::getPower()
{
return ( power );
}

int Character::getDefense()
{
return ( defense );
}


class ChDatabase
{
private:
vector<Character> database;

public:
ChDatabase( /*int slots*/ );
void addCharacter( Character thisCharacter );
void removeCharacter( Character thisCharacter );
void displayPlayer();
void displayAllEnemies();

};

ChDatabase::ChDatabase( /*int slots*/ )
{
// database.reserve( slots ); // not needed. The vector will grow as needed when we use the push_back function
}

void ChDatabase::addCharacter( Character thisCharacter )
{
database.push_back( thisCharacter );
}

void ChDatabase::removeCharacter( Character thisCharacter )
{

}

void ChDatabase::displayPlayer()
{
if( !database.empty() )
{
cout << " Name: " << database[0].getName() << endl;
cout << " Health: " << database[0].getHealth() << endl;
cout << " Attack: " << database[0].getPower() << endl;
cout << "Defense: " << database[0].getDefense() << endl << endl;
}
else cout << "No Player Exists." << endl;
}

void ChDatabase::displayAllEnemies()
{
if( database.size() <= 1 ) // changed
cout << "Database empty." << endl;
else
{
for( vector<Character>::iterator iter = (database.begin() + 1); iter != database.end(); ++iter )
{
cout << " Name: " << iter -> getName() << endl;
cout << " Health: " << iter -> getHealth() << endl;
cout << " Attack: " << iter -> getPower() << endl;
cout << "Defense: " << iter -> getDefense() << endl << endl;
}
}

}

ChDatabase db; // We need a database instance. Im making it global for now

int main()
{
srand(time(NULL));

string choice;

cout << "\tCreate-a-Character!\n\n";
cout << "What would you like to do?\n";
cout << "1 - Create your Character!\n"
<< "2 - Generate some enemies!\n"
<< "3 - Exit\n";

// The operator >> is too low level to be suitable for user input.
// Im using the getline function instaed...
getline(cin, choice);

switch( choice[0] )
{
case '1':
createCharacter();
break;
case '2':
//generateCharacter();
break;
case '3':
break;
default:
break;
}

cout << "Press enter to finish" << endl;
cin.get();

return 0;
}

void createCharacter()
{
string chname;
string keep;

//ChDatabase(1); // not needed (Im supprised this compiles at all)

Character* pplayer; // declaring this pointer outside the loop so it
// continue to exist after the loop

do
{

system("CLS");

pplayer = new Character;

cout << "\tWelcome to Character Creation!" << endl;
cout << "Here are your stats: " << endl;
cout << " Health: " << pplayer->getHealth() << endl;
cout << " Attack: " << pplayer->getPower() << endl;
cout << "Defense: " << pplayer->getDefense() << endl << endl;
cout << "Would you like to keep these (y/n)? ";
getline(cin, keep);

if( tolower( keep[0] ) == 'n' )
{
delete pplayer;
}
}
while( tolower( keep[0] ) == 'n' );

cout << "\n\nCharacter name: ";
getline(cin, chname);
pplayer->setName( chname );
db.addCharacter(*pplayer); // added this line
delete pplayer; // we can delete the heap object here since we already have a copy of it in the database

system("CLS");

cout << "\tCongratulations! Here is your character: " << endl << endl;
db.displayPlayer();

}

Share this post


Link to post
Share on other sites
So I made the changes you suggested and it worked, thank you! Now here's my next problem:



void generateEnemies()
{
system("CLS");

int num;

cout << "\tWelcome to the Enemy Generator!\n\n";
cout << "How many enemies would you like to generate? ";
cout << "1 - 2 - 3 - 4 ? ";
cin >> num;
switch( num )
{
case 1:
Character enemy1;
dBase.addCharacter( enemy1 );
break;
case 2:
Character enemy1, enemy2;
dBase.addCharacter( enemy1 );
dBase.addCharacter( enemy2 );
break;
case 3:
Character enemy1, enemy2, enemy3;
dBase.addCharacter( enemy1 );
dBase.addCharacter( enemy2 );
dBase.addCharacter( enemy3 );
break;
case 4:
Character enemy1, enemy2, enemy3, enemy4;
dBase.addCharacter( enemy1 );
dBase.addCharacter( enemy2 );
dBase.addCharacter( enemy3 );
dBase.addCharacter( enemy4 );
break;
default:
cout << "Invalid choice";
break;
}

main();

}




Here's the compile errors I get:



.\main.cpp(101) : error C2360: initialization of 'enemy1' is skipped by 'case' label
.\main.cpp(98) : see declaration of 'enemy1'
.\main.cpp(102) : error C2370: 'enemy1' : redefinition; different storage class
.\main.cpp(98) : see declaration of 'enemy1'
.\main.cpp(106) : error C2360: initialization of 'enemy2' is skipped by 'case' label
.\main.cpp(102) : see declaration of 'enemy2'
.\main.cpp(106) : error C2360: initialization of 'enemy1' is skipped by 'case' label
.\main.cpp(102) : see declaration of 'enemy1'
.\main.cpp(106) : error C2360: initialization of 'enemy1' is skipped by 'case' label
.\main.cpp(98) : see declaration of 'enemy1'
.\main.cpp(107) : error C2370: 'enemy1' : redefinition; different storage class
.\main.cpp(98) : see declaration of 'enemy1'
.\main.cpp(107) : error C2370: 'enemy2' : redefinition; different storage class
.\main.cpp(102) : see declaration of 'enemy2'
.\main.cpp(112) : error C2360: initialization of 'enemy3' is skipped by 'case' label
.\main.cpp(107) : see declaration of 'enemy3'
.\main.cpp(112) : error C2360: initialization of 'enemy2' is skipped by 'case' label
.\main.cpp(107) : see declaration of 'enemy2'
.\main.cpp(112) : error C2360: initialization of 'enemy1' is skipped by 'case' label
.\main.cpp(107) : see declaration of 'enemy1'
.\main.cpp(112) : error C2360: initialization of 'enemy2' is skipped by 'case' label
.\main.cpp(102) : see declaration of 'enemy2'
.\main.cpp(112) : error C2360: initialization of 'enemy1' is skipped by 'case' label
.\main.cpp(102) : see declaration of 'enemy1'
.\main.cpp(112) : error C2360: initialization of 'enemy1' is skipped by 'case' label
.\main.cpp(98) : see declaration of 'enemy1'
.\main.cpp(113) : error C2370: 'enemy1' : redefinition; different storage class
.\main.cpp(98) : see declaration of 'enemy1'
.\main.cpp(113) : error C2370: 'enemy2' : redefinition; different storage class
.\main.cpp(102) : see declaration of 'enemy2'
.\main.cpp(113) : error C2370: 'enemy3' : redefinition; different storage class
.\main.cpp(107) : see declaration of 'enemy3'
.\main.cpp(119) : error C2361: initialization of 'enemy4' is skipped by 'default' label
.\main.cpp(113) : see declaration of 'enemy4'
.\main.cpp(119) : error C2361: initialization of 'enemy3' is skipped by 'default' label
.\main.cpp(113) : see declaration of 'enemy3'
.\main.cpp(119) : error C2361: initialization of 'enemy2' is skipped by 'default' label
.\main.cpp(113) : see declaration of 'enemy2'
.\main.cpp(119) : error C2361: initialization of 'enemy1' is skipped by 'default' label
.\main.cpp(113) : see declaration of 'enemy1'
.\main.cpp(119) : error C2361: initialization of 'enemy3' is skipped by 'default' label
.\main.cpp(107) : see declaration of 'enemy3'
.\main.cpp(119) : error C2361: initialization of 'enemy2' is skipped by 'default' label
.\main.cpp(107) : see declaration of 'enemy2'
.\main.cpp(119) : error C2361: initialization of 'enemy1' is skipped by 'default' label
.\main.cpp(107) : see declaration of 'enemy1'
.\main.cpp(119) : error C2361: initialization of 'enemy2' is skipped by 'default' label
.\main.cpp(102) : see declaration of 'enemy2'
.\main.cpp(119) : error C2361: initialization of 'enemy1' is skipped by 'default' label
.\main.cpp(102) : see declaration of 'enemy1'
.\main.cpp(119) : error C2361: initialization of 'enemy1' is skipped by 'default' label
.\main.cpp(98) : see declaration of 'enemy1'
Build log was saved at "file://c:\Documents and Settings\Nate\My Documents\Visual Studio 2005\Projects\Aggregate\Aggregate\Debug\BuildLog.htm"
Aggregate - 26 error(s), 1 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========




What's the solution?

Also, is there a way to run a for loop a number of times that the user sets, and each iteration creates an object of type Character and adds it to the ChDatabase vector? What I don't understand is that you have to name the object when you create it, like Character enemy1, and I don't see how you could do that in a for loop. Any ideas? Becuase I couldn't figure this out I did the switch with the option of creating 1,2,3, or 4 enemies, a set amount, rather than asking the user how many they want to create then create that many.

Thanks for the help!

Share this post


Link to post
Share on other sites
Quote:

What's the solution?

We could declare all the objects before the switch but that is pesky.
A better solution is to tell the compiler to create the objects "on the fly" without giving them names. We can do that simply by calling the Character constructor:

void generateEnemies()
{
system("CLS");

string num;

cout << "\tWelcome to the Enemy Generator!\n\n";
cout << "How many enemies would you like to generate? ";
cout << "1 - 2 - 3 - 4 ? ";
getline(cin, num);
switch( num[0] )
{
case '1':
db.addCharacter( Character() );
break;
case '2':
db.addCharacter( Character() );
db.addCharacter( Character() );
break;
case '3':
db.addCharacter( Character() );
db.addCharacter( Character() );
db.addCharacter( Character() );
break;
case '4':
db.addCharacter( Character() );
db.addCharacter( Character() );
db.addCharacter( Character() );
db.addCharacter( Character() );
break;
default:
cout << "Invalid choice";
break;
}
}



Doing it this way you should be able to make it work in a for loop as well.

Also, don't ever call the main function yourself.
What we want is another loop in main:

...
bool done = false;

while(!done)
{
cout << "\tCreate-a-Character!\n\n";
cout << "What would you like to do?\n";
cout << "1 - Create your Character!\n"
<< "2 - Generate some enemies!\n"
<< "3 - Display all enemies!\n"
<< "4 - Exit\n";

getline(cin, choice);

switch( choice[0] )
{
case '1':
createCharacter();
break;
case '2':
generateEnemies();
break;
case '3':
db.displayAllEnemies();
break;
default:
done = true; // character creation done, quit the loop...
break;
}
}
...

Share this post


Link to post
Share on other sites
So if I don't have to name the objects, couldn't I do something as simple as:


cout << "How many bad people do you want to HELL-SPAWN(!)?: ";
cin >> spawn_hell_minions;

vector<Character> newvector( spawn_hell_minions, Character() );


Obviously this has nothing to do with my ChDatabase class, but isn't that correct syntax to declare and intialize a vector?

ALSO, it doesn't seem like the bool done/ while( !done ) loop would work the way my functions are working.

Share this post


Link to post
Share on other sites
Quote:
Original post by Shakedown
So if I don't have to name the objects, couldn't I do something as simple as:


cout << "How many bad people do you want to HELL-SPAWN(!)?: ";
cin >> spawn_hell_minions;

vector<Character> newvector( spawn_hell_minions, Character() );


That should work. By the way, you dont need to add the last parameter Character() since this is the default. If you want to fill the vector like this after creating it, you can use the assign method, or even the resize method. The resize method works a bit different though since it will preserve the already existing objects in the vector.

Quote:

ALSO, it doesn't seem like the bool done/ while( !done ) loop would work the way my functions are working.

I was just adding it so the program didn't finish before I could test the functions.

Share this post


Link to post
Share on other sites
Quote:
Original post by Shakedown
ALSO, it doesn't seem like the bool done/ while( !done ) loop would work the way my functions are working.


Why not? :)

Anyway, more changes to pulpfist's code. Explanations in-line. I removed most of the old comments as unnecessary ;)


#include <string>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <ctime>

// In the final code, we'll probably have class declarations factored out into
// header files. Since we don't want to put using-declarations into headers, I
// stuck our using-declaration after the end of all the class declarations, and
// use fully-qualified names inside the class bodies.

const string basenames[10] = { "Troll", "Ogre", "Ooze", "Gargoyle", "Dragon", "Orc", "Elemental", "Blood-Elf", "Goblin", "Gremlin" };

// Don't use function prototypes where you don't need them. In header files, we
// declare everything that needs to be "available" to multiple source files, but
// within the same implementation file, we should avoid them by instead
// reordering our functions. That way, we reduce maintenance work (by not having
// a function prototype that needs to match the declaration) and highlight any
// circular dependencies (which could indicate an undesired mutual recursion).

class Character {
std::string name;
int health;
int power;
int defense;

public:
// When all of the work of a constructor can be done from inside the
// initialization list, it's usual to inline the definition.
Character() : name(basenames[std::rand() % 10]),
health(std::rand() % 11 + 10),
power(std::rand() % 11 + 10),
defense(std::rand() % 11 + 10) {}
Character(std::string chname, int hp, int ap, int def) :
name(chname), health(hp), power(ap), defense(def) {}
// Don't write accessors or mutators until a need is proven for them.
// And even then, try really hard to find a better way to express the
// apparent need :)
// In our case, what we "need" the accessors for is simply to print
// instances of the class; the proper way to do this is to define how
// instances are printed, using an operator<< overload.
friend std::ostream& operator<<(std::ostream&, const Character&);
// And what we "need" the mutators for - and we only need the one for
// the name - is to read in a name for the player. This isn't the same
// as reading in a Character object, but we can still make use of the
// specificity:
void readNameFrom(std::istream&);
};

class CharacterDatabase { // not really worth abbreviating
vector<Character> database;

public:
// When a default constructor doesn't have to do anything, and it's the
// only constructor, we don't need to write it. ;)
// Normally, pass object instances by const reference rather than by
// value:
void addCharacter(const Character& c);
void removeCharacter(const Character& c);
void displayPlayer() const;
void displayAllEnemies() const;
};

using namespace std;

// Character implementation.

ostream& operator<<(std::ostream& os, const Character& c) {
// Don't use std::endl as a replacement for newlines.
// Within operator<< overloads, we don't generally flush the buffer,
// simply because the buffer isn't flushed by outputting anything else
// (except std::endl and std::flush themselves ;) ).
// Anyway, even though this is a free function, it's logically part of
// the Character interface, so we gave it special access to Character's
// members directly. Also note how we can do it all in one statement:
os << " Name: " << c.name
<< "\n Health: " << c.health
<< "\n Attack: " << c.power
<< "\nDefense: " << c.defense << "\n";
return os; // we do this so that we can continue "chaining" the operator
// with anything else that needs to be output.
// Notice how one return character is output here, and the invoking code
// will supply the other, generally. This is based on my judgement of
// what's really part of the Character "description", and what's
// in-between formatting.
}

void Character::readNameFrom(std::istream& is) {
// Notice how, by doing this part in a member function, we avoid the
// need for a temporary, because we can read directly into the member.
// This could be made to work using an accessor that returned a
// non-const reference, but that is a serious encapsulation leak here ;)
getline(is, name);
}

// CharacterDatabase implementation.

void CharacterDatabase::addCharacter(const Character& c) {
database.push_back(c);
}

// Don't implement things until you know how to implement them. That way, the
// compiler can complain about using functionality that doesn't really exist.

void CharacterDatabase::displayPlayer() const {
// Try to put the positive condition first.
if (database.empty()) {
cout << "No player exists." << endl;
} else {
// See how our operator overload works ;)
cout << database[0] << endl;
}
}

void CharacterDatabase::displayAllEnemies() const {
if (database.size() <= 1) {
cout << "No enemies exist." << endl;
// a little more accurate :)
} else {
// Our operator overload allows us to leverage the standard
// library for this printing task...
copy(database.begin() + 1, database.end(),
ostream_iterator<Character>(cout, "\n"));
// That reads: "Copy from 1 after the beginning of the database
// until the end of the database, to an iterator that will use
// the operator<< of Characters to output on std::cout, with
// "\n" text in between items." :)
cout << endl;
// finally add "\n" after the last Character, and flush.
}
}

// And now, our main program...
CharacterDatabase db;

// I'm making a helper function for getting user inputs:
char getCommand(istream& is) {
string line;
// Note that std::getline() doesn't put the delimiter character into
// the string... so if the user just hits return right away, 'line'
// will empty.
while (line.empty()) { getline(is, line); }
// Another way to handle the problem would be to just append '\n' to the
// string after reading it once: then, if the input is blank, '\n' gets
// returned.
return line[0];
}

// As suggested way earlier, createCharacter() is put first, so that we don't
// need to prototype it.
void createCharacter() {
// The reason "ChDatabase(1);" compiled is that it created an instance
// of the class with one reserved slot, and no name; and then promptly
// threw it away. :)

// Anyway, we don't need to use a pointer here at all.
Character c;
char keep;
do {
system("CLS");
c = Character();

// Again notice how we use the Character-display function in
// here (the operator overload). So we are benefitting from
// code reuse. ;) If we wanted to change, e.g. from displaying
// "Health:" to "HP:", now we only have to fix it in one place.
cout << "\tWelcome to Character Creation!\n"
<< "Here are your stats: \n"
<< c << "\nWould you like to keep these (y/n)? ";
keep = getCommand(cin);
} while (tolower(keep) == 'n');

cout << "\n\nCharacter name: ";
c.readNameFrom(cin);
db.addCharacter(c);

system("CLS");

cout << "\tCongratulations! Here is your character: \n\n";
db.displayPlayer();
}

int main() {
srand(time(NULL));

cout << "\tCreate-a-Character!\n\n"
<< "What would you like to do?\n";
<< "1 - Create your Character!\n"
<< "2 - Generate some enemies!\n"
<< "3 - Exit\n";

// We don't actually need a variable to hold the prompt result ;)
switch (getCommand(cin)) {
case '1':
createCharacter();
break;

case '2':
//generateCharacter();
break;

case '3':
break;

default:
break;
}
// Don't artificially pause your programs at the end, please.
// Also, there is no need to 'return 0' explicitly from main().
}

Share this post


Link to post
Share on other sites
Also:

#include <iterator>


change line 13 to
const std::string...


change line 53 to
std::vector...


and remove the semicolon from line 188 or she won't compile as written.

Even with these little issues, as is usually the case, I learn more from a single post by Zahlman than I do from any 5 chapters of any give Learn to Program C++ book.

Share this post


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

Even with these little issues, as is usually the case, I learn more from a single post by Zahlman than I do from any 5 chapters of any give Learn to Program C++ book.


Haha you're absolutely correct. I couldn't imagine anybody else posting another reply to this topic seeing as the problem has not only been solved, but an entire chapter in Zahlman's book has been written around the source code. Hah thank you again Zahlman, keep up the excellent work.

Share this post


Link to post
Share on other sites

This topic is 3860 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.

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