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().}