Pointer problem

Started by
12 comments, last by Shakedown 16 years, 11 months ago
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().}
Advertisement
Wow Zahlman...that was intense. Haha thank you, very much. I hope you reply to my most recent thread =)
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.
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.

This topic is closed to new replies.

Advertisement