I'm having trouble figuring out how to properly create an object depending on the user's choice.
In the program, I ask the user which they class they want to be--Knight or Wizard. I take input '1' or '2' to represent Knight and Wizard.
I made a switch statement, and within case 1, I declared an object Knight, and the same for Wizard.
I need to use these objects outside of the switch statement, but I can't. I tried to make a 'default' object by making 'Player player;' but because the Player class has a pure virtual function, I can't do that either.
How do I do this effectively?
This is what I have so far:
int main()
{
std::string plyrName;
int input;
bool foo = false;
std::cout << "What is your name?\n";
std::cin >> plyrName;
std::cin.ignore(1000, '\n');
std::cout << "\nWelcome, " << plyrName << ". What class would you like to be?\n";
std::cout << "1. Knight.\n2. Wizard.\n";
std::cin >> input;
while (input != 1 && input != 2)
{
if (foo == true)
std::cout << "Please enter 1 for Knight and 2 for Wizard.\n";
if (!(std::cin >> input))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "\n";
std::cout << "Only integers are allowed.\n";
}
else
std::cout << "\n";
foo = true;
}
switch (input)
{
case 1:
{
Wizard player;
break;
}
case 2:
{
Knight player;
break;
}
}
std::cout << "\nHere is your player information summary.\n";
std::cout << player.classType();
system("pause");
return 0;
}
I need to access the player object after is has been created, because I want to output to the user which class they selected. Both Knight and Wizard classes have a function to output this.
EDIT: I have a follow up question. In the diagram, Knight & Wizard have a static variable 'special attack name'. How can I access this variable in the main function? The solution of using unique_ptr means that the pointer will point to the base class Player, thus not allowing access to the derived class members such as the static variable 'special attack name'. Do I have a flaw in my design?
In your case, because you want to accomplish polymorphism, you should go for pointers and references. Why? I would highly recommend this beautiful answer. Why doesn't polymorphism work without pointers/references?
So, should you go for a raw pointer, something like Player *?
In almost all scenarios, you should never ever go for raw pointers and especially, when it points to dynamic memory. Simply because any programming error or an exception might lead to delete getting skipped.
Therefore, I would highly recommend you to go for smart pointers introduced in C++11 like unique_ptr and shared_ptr which follow RAII pattern and guarantee deinitialization.
Here is an example of usage of unique_ptr in your case.
#include <memory>
using PlayerPtr = std::unique_ptr<Player>;
using KnightPtr = std::unique_ptr<Knight>;
using WizardPtr = std::unique_ptr<Wizard>;
int main()
{
...
PlayerPtr playerPtr = nullptr;
switch (input) {
case 1: {
playerPtr = KnightPtr(new Knight);
}
break;
case 2: {
playerPtr = WizardPtr(new Wizard);
}
break;
}
// use playerPtr outside.
}
Edit:
As rightly pointed out by HTNW, you must go for std::make_unique instead of using new. But remember, that is a C++14 concept. You must have compiler support for it.
if you create your variable inside the switch case scope it will get deleted as soon as you leave that scope leading you to UB so you declare it as a pointer so it can outlive the conditional statement ie : you declare it as the base class pointer before & you give it to where it points inside conditional statement
#include<memory>
int main()
{
std::string plyrName;
int input;
bool foo = false;
//create your player ptr
std::unique_ptr<Game_Object> player;
std::cout << "What is your name?\n";
std::cin >> plyrName;
std::cin.ignore(1000, '\n');
std::cout << "\nWelcome, " << plyrName << ". What class would you like to be?\n";
std::cout << "1. Knight.\n2. Wizard.\n";
std::cin >> input;
while (input != 1 && input != 2)
{
if (foo == true)
std::cout << "Please enter 1 for Knight and 2 for Wizard.\n";
if (!(std::cin >> input))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "\n";
std::cout << "Only integers are allowed.\n";
}
else
std::cout << "\n";
foo = true;
}
switch (input)
{
case 1:
{ // initialize it and it would work perfectly as you intend
player = std::make_unique<WIZARD>();
break;
}
case 2:
{ //****
player = std::make_unique<KNIGHT>();
break;
}
}
std::cout << "\nHere is your player information summary.\n";
std::cout << player->classType();
system("pause");
return 0;
}
Someone correct me if I'm wrong, but objects are only valid within the scope they are created so once you are out of that switch statement those objects are not accessible anymore.
You should declare a GAME OBJECT class object outside the switch statement and then create either the wizard or the knight (as both classes inherit GAME OBJECT).
Related
New to C++ OOP, I recently learned about classes and objects. I created a straightforward class and menu-driven program that adds a temp object to a vector of movies. I have a quick question that I can't quite understand.
Am I just pushing multiple "temp" objects into the vector?
In my head i'm visualizing this as vector my_movies = {temp, temp, temp}; and continously adding 'temp' objects until the user is done. Is this the right way to picture it?
#include <iostream>
#include <vector>
using namespace std;
class Movie
{
private:
string name;
public:
string get_name() { return name; }
void set_name(string n) { name = n; }
};
void menu() {
cout << "1. Add movie" << endl;
cout << "2. Show Movies" << endl;
cout << "3. Quit" << endl;
}
int getChoice(int &choice) {
cout << "Enter you choice: ";
cin >> choice;
return choice;
}
int main() {
vector<Movie> my_movies;
int choice = 0;
string name;
do {
menu();
getChoice(choice);
switch (choice) {
case 1: {
Movie temp;
cout << "Set user name: ";
cin >> name;
temp.set_name(name);
my_movies.push_back(temp);
break;
}
case 2: {
for (auto &mv : my_movies)
cout << mv << endl;
break;
}
}
} while (choice != 3);
return 0;
}
In your case, when you are calling push_back it will copy your "temp" object, which is a local object on the stack. It will be copied into a new object which is stored on the heap, held by the vector object. The vector will store these as an array internally (the default vector with the default allocator etc).
It's also possible to "move" the object (under C++11 and later), if you understand the difference, but doing push_back(std::move(temp)), which generally gives better performance. In your case it would avoid copying the string member "name", and move it instead, avoiding a new allocation for the string inside the Movie in the vector.
See here for more details on push_back
Appends the given element value to the end of the container.
The new element is initialized as a copy of value.
https://en.cppreference.com/w/cpp/container/vector/push_back
If you are just talking about the name of the movie, it will be what ever is entered from cin. Objects don't have names themselves. The local variable name "temp" is just what you see when you write the code, but is just used to tell the compiler which object is being used - the object itself doesn't have a name form the compilers perspective.
I've been working on a Shape program lately ( Some of you might remember my other questions about this... ;/ ) And I have a tiny problem which I want to fix.
In my Menu class, which holds all the functions related to the menu. I have a unique_ptr vector with the type of my base class Shape which holds all of the newly created objects ( Circles, Rectangles, ect )
protected:
vector<unique_ptr<Shape>> _shapes;
One of the functions that I want to create is supposed to change the values of the variables in a given shape based on it's index. To do so, I was planning to print the vector to the user, and then let him to choose the index of the shape that he wants to change.
void Menu::printShapes() const
{
int i = 0;
for (auto p = _shapes.begin(); p != _shapes.end(); p++, i++)
{
cout << i + " ";
(*p)->printDetails();
cout << endl;
}
}
The problems lays in my main program which is going to use my Menu functions. Because I don't want the user to be able to enter values which are outside of my vector, I have to check if the given input is between 0 and the size of the vector. But I cannot access this info from my main function without making the vector public or make a return statement from the printShapes() function, which will make the code messy and not intuitive as I want it to be.
So my question is: Is there a way to find the size of the vector at the Menu function from the main function without making the changes I stated above? Because in the end I want to be able to just do menu.printShapes() and then let the user to choose the index of the shape that he wants to change
this is my main function as of now:
Menu menu;
int input = 0, wait = 0;
while (input != 4)
{
cout << "1: Add New Shape: " << endl;
cout << "2: Modify Existing Shape: " << endl;
cout << "3: Delete All Shapes: " << endl;
cout << "4: Exit: " << endl;
while (input < MIN || input > MAX)
{
cin >> input;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
std::cin >> wait;
}
switch (input)
{
case 1:
{
cout << "1: Circle: " << endl;
cout << "2: Rectangle: " << endl;
cout << "3: Triangle: " << endl;
cout << "4: Arrow: " << endl;
while (input < MIN || input > MAX)
{
cin >> input;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
std::cin >> wait;
}
menu.createShape(input);
}
case 2:
{
/*
I want to be able to access the size of the vector from here
So I could do something like that:
menu.printShapes();
while (input < 0 || input > vectorSize)
{
:Get the index of the shape that the user wants to modify
}
Instant of doing
size = menu.printShapes();
*/
}
}
}
You're way overthinking this.
You just need to add a public function that returns the current size of the vector:
// Declaration in your class
size_t numberOfShapes() const;
// Definition
size_t Menu::numberOfShapes() const
{
return _shapes.size();
}
. Then, when you want to know the size, call that function.
menu.printShapes();
while (input < 0 || input > menu.numberOfShapes())
{
// Get the index of the shape that the user wants to modify
}
Simple!
By the way, I think you meant >= there, not >.
You have at least a few options; depending on the details of the modify-existing-shape operation.
The easiest thing would probably be to have your Menu type expose a method to tell you how many shapes it's managing. I agree you shouldn't make the vector public because the program doesn't need to know it's a vector specially, but if "knowing how many existing shapes the menu is managing" is a requirement (which it seems to be) then exposing it seems reasonable.
Alternatively, depending on the return type of the modify operation you could return an optional or a variant. If you have 7 shapes and I ask you to modify the 6th then you might tell me that it worked or maybe the new dimensions, but if I ask you to modify the 9th you might tell me it's an invalid index.
The difference becomes whether to obligate the caller to be informed and ask a valid question, or whether you want to be more robust and answer and handle a broader domain of questions. I don't think it makes a big difference in this case but I tend toward the second kind of solution simply because it means any potential caller is protected from going out of range instead of all of then having to check the count then do their own validation.
For my homework assignment I'm supposed to make a create-your-own-adventure story. There are certain words in the text that are in all caps to represent boolean values that I need to display at the end if the player got them, like a status effect or something. I'm having trouble figuring out how to pass the booleans to the functions so that it makes it to the end of the program where I can display it. My program has functions within functions.
I've tried making the function that sets the boolean to true a boolean itself, then returning the boolean but that just ends the program it seems. I've also tried passing it through the first function call to see if it reaches the second but it doesn't seem like it wants to.
void A1();
bool A100(bool INTIM);
void A167();
void A232();
void A290();
void A13();
void A212();
void A173();
void A159();
void A161();
int main() {
bool INTIM;
A1();
cout << INTIM << endl;
return 0;
}
void A1()
{
int choice;
cout << "Well, Mr Artanon, ...\n 1. ’It’s you who’ll get a rare cut
across that corpulent neck of yours if you don’t speed things along, you
feckless blob of festering lard.’\n 2. ’Surely in such an industrious
kitchen, there must be a starter or two ready to send along and sate His
Abhorentness’s appetite?’\n (enter a menu option): ";
cin >> choice;
while (choice != 1 && choice != 2)
{
cout << "Enter in a valid choice (1 or 2)";
cin >> choice;
}
if (choice == 1)
{
A100();
}
if (choice == 2)
{
A167();
}
}
bool A100(bool INTIM)
{
int choice;
INTIM = true;
cout << " Repugnis turns a paler...\n 1. Onwards, Mr Artanon.\n (enter
in a menu option): ";
cin >> choice;
while (choice != 1)
{
cout << "Enter in a valid option (1)";
}
return INTIM;
A232();
}
What I'm wanting to happen is, the bool INTIM to be passed along so i can display it back in main with the cout statement. I know it will just be a 1 or 0 at the end but I'm just trying to get it to show up at least in the end when I display it. Again there are functions within functions in this program and that might be my problem but I wouldn't think so. There is also functions that come after this, this is not the end of the program and if I need to post the whole thing I will
Calling A100 as written, you need to pass in INTIM and accept the return value
INTIM = A100(INTIM);
But... The initiqal state of INTIM is never used, so you could
INTIM = A100();
and change A100 to look more like
bool A100()
{
int choice;
cout << " Repugnis turns a paler...\n 1. Onwards, Mr Artanon.\n (enter in a menu option): ";
cin >> choice;
while (choice != 1)
{
cout << "Enter in a valid option (1)";
cin >> choice; // added here because otherwise choice never changes
// and this loop will go on for a long, long time.
}
A232(); // moved ahead of return. Code after a return is not run
return true;
}
But since A232 is called and may set additional flags you cannot return, you have a design flaw: What if A232 also modifies a boolean? You can only return one thing from a function. You could pass A232's boolean in by reference, but what it A232 then calls B484 and it also has a boolean?
You don't want to have to pass around every possible boolean, that would be a confusing mess, so consider making a data structure that stores all of your booleans to pass around.
And that leads to an even better idea: encapsulating the booleans and the functions in the same data structure so that you don't have to pass anything around; it's all in the same place.
Do I need to pass them [the boolean results] to the functions?
Often, but not always, it is my preference to pass them by reference, and yes, it can get to be a big chain thru many functions. sigh.
But your question is "Do you need to pass them ...".
The answer is No.
Because
a) you have tagged this post as C++, and
b) the key feature of C++ is the user-defined-class.
Consider declaring every 'adventurous function' of your story within a class scope.
Each 'adventurous function', as an attribute of the class, is implemented with one 'hidden' parameter, the 'this' pointer to the class instance.
So .. if you place all your 'result' booleans as data attributes of the class, invoking any 'adventurous function' will also 'pass' all the class instance data attributes (all your bools!) as part of the invocation. No data is actually moving, just a pointer, the 'this' pointer.
It might look something like this:
#include <iostream>
using std::cout, std::cerr, std::flush, std::endl;
// using std::cin;
#include <iomanip>
using std::setw, std::setfill;
#include <sstream>
using std::stringstream;
#include <string>
using std::string;
namespace AS // Adventure Story
{
class CreateYourOwnAdventureStory_t
{
private:
// diagnostic purposes
stringstream ssUI;
// command line arguments concatenated into one string
// contents: strings convertable to ints to mimic cin
bool INTIM;
// other results go here
public:
int operator()(int argc, char* argv[]) {return exec(argc, argv);}
private:
int exec(int argc, char* argv[])
{
int retVal = 0;
// capture all command line arguments into a string
for (int i=1; i<argc; ++i)
ssUI << argv[i] << " ";
cout << "\n ssUI: " << ssUI.str() << "\n\n\n";
A1();
cout << "\n INTIM : " << INTIM << endl;
// ?more here?
return retVal;
}
void A1()
{
int choice = 0;
cout << "Well, Mr Artanon, ...\n "
"\n 1. ’It’s you who’ll get a rare cut across that corpulent neck of yours "
"if you don’t speed things along, you feckless blob of festering lard. "
"\n 2. ’Surely in such an industrious kitchen, there must be a starter or two "
"ready to send along and sate His Abhorentness’s appetite?’"
"\n (enter a menu option): ";
ssUI >> choice; // cin >> choice;
if (choice == 1) { A100(); }
if (choice == 2) { A167(); }
}
void A100()
{
int choice = 0;
INTIM = true;
ssUI >> choice; // cin >> choice;
cout << "\n\n A100() choice:" << choice
<< " INTIM: " << INTIM << endl;
}
void A167()
{
int choice = 0;
INTIM = false;
ssUI >> choice; // cin >> choice;
cout << "\n\n A167() choice:" << choice
<< " INTIM: " << INTIM << endl;
}
// other action-functions go here
}; // class CreateYourOwnAdventureStory_t
typedef CreateYourOwnAdventureStory_t CreateYOAS_t;
} // namespace AS
int main(int argc, char* argv[]){return AS::CreateYOAS_t()(argc,argv);}
Notes:
This example grabs the command line parameters and appends them to a string stream. The result is use-able in a fashion much like your cin statements.
Did you notice you (probably) will not need forward declarations for your functions? The compiler has to scan a lot of the class declaration to decide various issues, and thus can figure out that A100 (and A167) are actually with-in the scope of AS::CreateYOAS_t::. The functions can still be moved into a cpp file, so you can still take advantage of separate compilation. (and maybe save some effort compiling smaller files, and only the changed files.)
Did you notice that the functions accessing INTIM simply use the bool, without needing any 'this->' to de-reference?
Main invokes a simple Functor. Nothing else. Main invokes operator(). Simple, minimal. The ctor and dtor are currently default. If you need to use the ctor to initialize results or other intermediate info, I would simply add it near the operator() implementation.
PS: You mentioned using bools to return results. You might as, an alternative, consider using a stringstream ... a single stream with text ... use like a log for capturing the ongoing game, or for a single simple overall report to the user.
Good luck.
I'm totally new and I don't know how else to ask this or what to even search for.
The case is this: I want to navigate through a menu with several sub-menus. In this example I'll just use "options" and a "game" to illustrate what I mean. Say you have a menu with 3 options.
1 - Start
2 - Options
3 - Quit
Choosing options should take you to another menu. Which would then look something like
1 - Difficulty
2 - Sound
3 - Back
Depending on where you go from here, there will be more sub menus obviously.
I've tried nesting do-while loops and all kinds of things but I just don't have enough understanding to know what it is I'm doing wrong.
Here is what I have so far:
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int choice;
do{
cout << "Main Menu\n";
cout << "Please make your selection\n";
cout << "1 - Start game\n";
cout << "2 - Options\n";
cout << "3 - Quit\n";
cout << "Selection: ";
cin >> choice;
switch(choice) {
case 1:
cout << "Pew pew!\n";
break;
case 2:
cout <<"????\n";
break;
case 3:
cout << "Goodbye!";
break;
default:
cout << "Main Menu\n";
cout << "Please make your selection\n";
cout << "1 - Start game\n";
cout << "2 - Options\n";
cout << "3 - Quit\n";
cout << "Selection: ";
cin >> choice;
}
} while(choice !=3);
system("PAUSE");
return EXIT_SUCCESS;
}
Which works like a regular menu. But I have no idea where to go from here. I consulted some books, but finding anything even remotely related to this was completely random. Any help or examples would be greatly appreciated.
What happened with nesting tons of loops just made all loops execute simultaneously every time. How do I keep this from happening? Making more choices? (choice1-2-3 etc ? or what?)
Ok guys. Thanks for all the help. This is what I ended up with in the end.
It runs as I want it to and by max_'s example and Mike B's commentary I think this works pretty well.
Thanks alot everyone =)
#include <iostream>
#include <cstdlib>
using namespace std;
void menu();
void mainMenu();
void optionsMenu();
void options();
int choice1 = 0;
int choice2 = 3;
int main(int argc, char** argv) {
menu();
return 0;
}
void menu(){
do {
choice2 = 0;
mainMenu();
switch(choice1) {
case 1:
cout << "Pew pew!\n";
break;
case 2:
options();
break;
case 3:
break;
}
} while(choice1 != 3);
}
void options(void) {
do {
optionsMenu();
switch(choice2){
case 1:
cout << "So difficult!\n";
break;
case 2:
cout << "Beep!\n";
break;
case 3:
break;
default:
break;
}
} while(choice2 != 3);
}
void mainMenu(void) {
cout << "Main Menu\n";
cout << "1 - Start game\n";
cout << "2 - Options\n";
cout << "3 - Quit\n";
cout << "Please choose: ";
cin >> choice1;
}
void optionsMenu(void) {
cout << "Options Menu\n";
cout << "1 - Difficulty\n";
cout << "2 - Sound";
cout << "3 - Back\n";
cout << "Please choose: ";
cin >> choice2;
}
How about this (dunno if it compiles though):
#include <cstdlib>
#include <iostream>
using namespace std;
int GetInput()
{
int choice;
cin >> choice;
return choice;
}
void DisplayMainMenu()
{
cout << "Main Menu\n";
cout << "Please make your selection\n";
cout << "1 - Start game\n";
cout << "2 - Options\n";
cout << "3 - Quit\n";
cout << "Selection: ";
}
void DisplayOptionsMenu()
{
cout << "Options Menu\n";
cout << "Please make your selection\n";
cout << "1 - Difficulty\n";
cout << "2 - Sound\n";
cout << "3 - Back\n";
cout << "Selection: ";
}
void Options()
{
int choice = 0;
do
{
system("cls");
DisplayOptionsMenu();
choice = GetInput();
switch(choice)
{
case 1:
cout << "difficulty stuff";
break;
case 2:
cout << "sound stuff";
break;
case 3:
break;
default:
break;
}
} while(choice!=3);
}
int main(int argc, char *argv[])
{
int choice = 0;
do
{
system("cls");
DisplayMainMenu();
choice = GetInput();
switch(choice) {
case 1:
cout << "Pew pew!\n";
break;
case 2:
Options();
break;
case 3:
cout << "Goodbye!";
break;
default:
break;
}
} while(choice!=3);
system("PAUSE");
return EXIT_SUCCESS;
}
I'd recommend that you change a few things here. Are you familiar with object-oriented design? If not, it's highly recommended that you read about that if you're looking to write code in C++ (Or just writing code in general, as it's a pretty major aspect of many programming languages)
Consider treating each of your menus and submenus as individual objects. Each time you enter the loop, use an object pointer to call a method that prints the current menu text.
Then, take the input from the user as normal, and change the menu object you're using now.
This is perhaps not the most ideal way to do a console menu, but it will give you a very strong grounding in how objected-oriented programming works.
I've attached an example :
#include <iostream>
#include <string>
class BaseMenu
{
public:
BaseMenu() { m_MenuText = "This shouldn't ever be shown!"; } // This is the constructor - we use it to set class-specific information. Here, each menu object has its own menu text.
virtual ~BaseMenu() { } // This is the virtual destructor. It must be made virtual, else you get memory leaks - it's not a quick explaination, I recommend you read up on it
virtual BaseMenu *getNextMenu(int iChoice, bool& iIsQuitOptionSelected) = 0; // This is a 'pure virtual method', as shown by the "= 0". It means it doesn't do anything. It's used to set up the framework
virtual void printText() // This is made virtual, but doesn't *have* to be redefined. In the current code I have written, it is not redefined as we store the menu text as a string in the object
{
std::cout << m_MenuText << std::endl;
}
protected:
std::string m_MenuText; // This string will be shared by all children (i.e. derived) classes
};
class FirstMenu : public BaseMenu // We're saying that this FirstMenu class is a type of BaseMenu
{
FirstMenu()
{
m_MenuText = "Main Menu\n" // What we are doing here is setting up the string to be displayed later
+ "Please make your selection\n" // What we are doing here is setting up the string to be displayed later
+ "1 - Start game\n" // What we are doing here is setting up the string to be displayed later
+ "2 - Options\n" // What we are doing here is setting up the string to be displayed later
+ "3 - Quit\n" // What we are doing here is setting up the string to be displayed later
+ "Selection: "; // What we are doing here is setting up the string to be displayed later
}
BaseMenu *getNextMenu(int choice, bool& iIsQuitOptionSelected) // This is us actually defining the pure virtual method above
{
BaseMenu *aNewMenu = 0; // We're setting up the pointer here, but makin sure it's null (0)
switch (choice) // Notice - I have only done "options". You would obviously need to do this for all of your menus
{
case 2:
{
aNewMenu = new SecondMenu; // We're creating our new menu object here, and will send it back to the main function below
}
case 3:
{
// Ah, they selected quit! Update the bool we got as input
iIsQuitOptionSelected = true;
}
default:
{
// Do nothing - we won't change the menu
}
}
return aNewMenu; // Sending it back to the main function
}
};
class SecondMenu : public BaseMenu
{
SecondMenu()
{
m_MenuText = "OptionsMenu\n"
+ "Please make your selection\n"
+ "1 - ????"
+ "2 - dafuq?";
}
BaseMenu *getNextMenu(int choice, bool& iIsQuitOptionSelected) // This is us actually defining the pure virtual method above
{
BaseMenu *aNewMenu = 0; // We're setting up the pointer here, but makin sure it's null (0)
switch (choice) // Notice - I have only done options. You would obviously need to do this for all of your menus
{
case 1:
{
aNewMenu = new FirstMenu; // We're creating our new menu object here, and will send it back to the main function below
}
break;
case 2:
{
aNewMenu = new FirstMenu; // We're creating our new menu object here, and will send it back to the main function below
}
break;
default:
{
// Do nothing - we won't change the menu
}
}
return aNewMenu; // Sending it back to the main function
}
};
int main (int argc, char **argv)
{
BaseMenu* aCurrentMenu = new FirstMenu; // We have a pointer to our menu. We're using a pointer so we can change the menu seamlessly.
bool isQuitOptionSelected = false;
while (!isQuitOptionSelected) // We're saying that, as long as the quit option wasn't selected, we keep running
{
aCurrentMenu.printText(); // This will call the method of whichever MenuObject we're using, and print the text we want to display
int choice = 0; // Always initialise variables, unless you're 100% sure you don't want to.
cin >> choice;
BaseMenu* aNewMenuPointer = aBaseMenu.getNextMenu(choice, isQuitOptionSelected); // This will return a new object, of the type of the new menu we want. Also checks if quit was selected
if (aNewMenuPointer) // This is why we set the pointer to 0 when we were creating the new menu - if it's 0, we didn't create a new menu, so we will stick with the old one
{
delete aCurrentMenu; // We're doing this to clean up the old menu, and not leak memory.
aCurrentMenu = aNewMenuPointer; // We're updating the 'current menu' with the new menu we just created
}
}
return true;
}
Note that this might be a bit complex for starting out. I strongly recommend you read the other answers people have posted. It should give you a few approaches on how to do it, and you can progress from the basic up to the more complex, examining each change.
Looking at what you are trying to do, I would change how you are ensuring the user still want's to play the game first. Look at using a while loop to check if a variable is true or false (people tend to use boolean variables(bool's) for this, an int set to 1 or 0 will do the same). That removes the need for the do-while. Reading up on control logic (if/else, while, for loops) and logical operators (&& - and, || - or, != - not equal to) is recommended. Control logic makes your code do different things, booleans are quick for checking yes/no scenarios and logical operators allow you to check multiple items in one if statement.
Some reading: Loops
Edit: Have more links for reading material, don't have the rep to post them.
Secondly, use another variable (int or whatever suits you) to track what screen you are on.
Based on this selection, display different options but still take input 1,2,3 to decide upon the next action.
In some terrible pseudo-code here is what I would lean towards:
main()
{
int choice
int screen = 1
bool running = true
while(running) {
//Screen 1, Main menu
if(screen == 1) {
cout << stuff
cout << stuff
cout << option 1
cout << option 2
cout << option 3
cout << selection:
cin >> choice
}
else if(screen == 2){
//options screen here
}
else {
//default/error message
}
//add some choice logic here
if(screen == 1 && choice == 3){
//being on screen one AND choice three is quit
running = false;
}
else if(screen == 1 && choice == 2){
//etc..
}
}
}
This is my first proper answer, all terrible criticism is well recieved.
I have five files: class T, class M (an abstract class), class MC (a container), class AC (creates a particular object that is added into the MC container) and my Main file.
I have these functions to add an object (for this case, AC) and to retrieve a data member that you find in AC (a title).
The program compiles and it appears that I can create and add an AC object. However when I try to use my GetTitle function, the program crashes and I get the following error
“Unhandled exception at 0x00b938e6 in TLab 5.exe: 0xC0000005: Access
violation reading location 0xcccccce4.”
From what I looked up, this means I have a pointer that is bad/uninitialized. The only pointer in my program is this:
M *C[MCSize] //Found in MC.h
The constructor for MC looks like this:
MC::MC()
{
cout << "Enter Name: ";
getline(cin, CName);
cout << "Enter size of collection: ";
cin >> CurrentMCSize;
if (CurrentMCSize < 0 || CurrentMCSize > MCSize)
{
cout << "Size is invalid. Please re-enter: ";
cin >> CurrentMCSize;
}; //MCSize is defined in the header of MC.
The function to call the Title that is entered is here:
void MC::ListMTitles()
{
for (int i = 0; i < CurrentMCSize; i++)
{
cout << i << ". " << Collection[i]->GetTitle();
}
};
//GetTitle is defined in M.cpp
Where DMA occurs: //MC.cpp
void MC::AddM()
{
int Selection;
if(CurrentMCSize < MCSize)
{
DisplayMTypeMenu();
Selection = GetMTypeSelection();
switch(Selection)
{
case 1: Collection[CurrentMCSize] = new AC;
break;
// Other case statements
}
if (0 == Collection[CurrentMCSize])
{
cout << "Error: Memory Allocation Failed.";
exit(1);
}
else
{
cout << "New M Type added!" << endl << endl;
}
CurrentMCSize++;
}
Have I not properly initialized my pointer? Is my Add function actually lying to me and nothing is being added? I looked around but most answers I saw involved using a vector, which for the sake of this project I don’t think I’m allowed to use as the professor didn’t go over them.
You are asking the user to input the size of the collection during construction, but you never populate those elements of the collection. Then, when you call AddM, it continues from CurrentMCSize. You should instead initialize CurrentMCSize to zero in the constructor and not ask for it at all.
The problem occurs because this
for (int i = 0; i < CurrentMCSize; i++)
{
cout << i << ". " << Collection[i]->GetTitle();
}
starts at 0 but there is no guarantee that AddM() will begin adding at 0:
void MC::AddM()
{
int Selection;
if(CurrentMCSize < MCSize)
{
DisplayMTypeMenu();
Selection = GetMTypeSelection();
switch(Selection)
{
case 1: Collection[CurrentMCSize] = new AC;
Instead it will add at whatever CurrentMCSize is which could be fed into the constructor as something like 4. You have three separate values you want to track: max supported size, the size presently used and the next slot to allocate an item but you've collapsed the last two into one variable.
Related question - Any reason you don't want to simply use a std::vector and push_back?
Edit: Ah I didn't see it, Paddy beat me to it.