I'm relatively new to C++ (and programming in general for the most part) and I'm taking an online course which follows along by teaching with the aim of building a game. In one of the console application projects we have to write code which asks the user if the user wants play the game again.The goal for that part of the program is to ask the user to reply by typing "y" or "n" only and re-ask the question if the user responds by doing something else.
I have attempted to write code which does this and as far as I can see the code works fine. My question is, is the code I wrote valid in the following sense:
Will the code successfully accomplish the outlined task?
Is code like this prone to more errors if it grows later?
The code I have follows. In the main function:
.
.
.
do
{
PlayGame();
}
while (AskToPlayAgain());
And the function definition for AskToPlayAgain along with an apt description:
bool AskToPlayAgain()
{
bool Play = true;
string response = "";
cout << "Would you like to play again? y/n... ";
getline(cin, response);
if (response == "y") { }
else if (response != "n")
{
cout << "Please enter a valid response." << endl;
// We want AskToPlayAgain called again to ask for a proper response again; however,
// just calling it outside a conditional will cause the current "Play" value to be returned.
// This value is true by default and this function will return it even if "n" is entered after
// an invalid response. As such, in this case, we want this function to be called in the event
// of an invalid response which will always happen and we don't want the return value of the nested
// AskToPlayAgain function to be returned, which is true by default unless the program has successfully exited.
// Furthermore, once the nested AskToPlayAgain returns false, we want the program to exit and not return the "Play"
// (which is set to true) by the higher level AskToPlayAgain.
if (AskToPlayAgain()) { }
else { return false; }
}
else
{
Play = false;
cout << "Thank you for playing! :D" << endl;
}
return Play;
}
Is the reasoning I presented in the code comments valid? Is there a test case where this would fail? I've tried a few test cases, but all of them worked.
Many thanks for any help on this!
There is nothing wrong with your recursive method but you can simplify this with a loop, and avoid the potential problems associated with recursion. Loops and recursion are closely related. if (response == "y") { } is not wrong but it's an odd programming practice. If you are not going to do anything upon reaching this condition then don't bother testing for it.
Another method with while loop:
bool AskToPlayAgain()
{
while(true)
{
string response;
cout << "Would you like to play again? y/n... ";
getline(cin, response);
if(response == "y")
{
return true;
}
else if(response == "n")
{
cout << "Thank you for playing! :D" << endl;
return false;
}
cout << "Please enter a valid response." << endl;
}
}
Edit
Another example with recursive function:
This time we add a counter value for demonstration.
If you run this program and keep giving invalid input, then counter will go up. It shows how the recursive function needs to wait for all other recursive functions to finish.
I added char buf[1000] which is not used. Its purpose is to cause problems!
Potential problem: each function needs to allocate 1000 bytes stack memory (plus memory for other stack variables in the function, and heap memory for std::string). This memory is not freed until the function exists, so it builds up. Stack limit in your program is a few mega bytes, so now there is a potential stack-overflow error.
bool AskToPlayAgain(int counter)
{
char buf[1000]; //<-- allocate lots of stack memory to cause problems!
cout << "counter start: " << counter << " - memory allocated\n";
cout << "play again? y/n... ";
string response;
getline(cin, response);
if(response == "y")
{
return true;
}
else if(response == "n")
{
cout << "Thank you for playing! :D\n";
return false;
}
cout << "invalid response\n";
bool result = AskToPlayAgain(counter + 1);
cout << "counter: " << counter << " - memory cleanup\n";
return result;
}
int main()
{
do
{
printf("play...\n");
} while(AskToPlayAgain(1));
return 0;
}
For this reason its better to use loops in favor of recursive functions. But then again, recursive functions are sometimes useful, and if memory allocation is under control (like in your example) and there is clear path to break the recursion, then go ahead and use it.
i would suggest that in your AskToPlayAgain() function, you do the checking of response == "y" and response == "n" and then do your else section. Although this won't affect your code much, it is certainly easier to read and understand, and if an issue does come up later on , you will not have to spend much time having to go through your code again.
i.e.
if (response == "y") { }
else if (response == "n"){
// your code
}
else {
// your code to handle the invalid response
}
Plus, as suggested in the comments and in the answer by #Barmak Shemirani, It would be better to just use a loop to accomplish this task of repeated asking until a valid response. It's "cheaper" than having multiple function calls.
Related
So, I'm triying to learn c++ (coming from python), and I wanted to make a program just to see if i could do it with what i've learned, here's the code
#include <iostream>
using namespace std;
int response(string i) {
if (i == "yes" or i == "Yes") {
cout << "\nHello, sad, I'm dad\n";
return(0);
}
else if (i == "no" or i == "No") {
cout << "Good for you pal\n";
return(0);
}
else {
cout << "Answer properly you overgrown flatworm\n";
response(i);
};
};
int main() {
string i;
cout << "Are you sad?";
cin >> i;
response(i);
};
Pretty simple huh? No. For some reason, yes and no answers work fine, but when I try something different I get insulted infinitely and the program crashes from exceeding it's memory limit. How do I solve this?
(English is not my native language, so feel free to correct any ortography mistakes)
At no point do you request further input. For bad input 'i', the response routine prints out an insult, and then calls itself with exactly the same string.
The response routine prints out an insult, and then calls itself with exactly the same string.
The response routine prints out an insult, and then calls itself with exactly the same string.
…
You need to allow the user to enter a new string, and then (if you want to use recursion) make the recursive call to validate the new input.
But as mentioned in the comment, this is not really a problem that needs a recursive solution.
This can be solved by eliminating recursion ad it involves moving the input routine inside of a function that's more self-contained:
int getResponse(string i) {
for(;;) {
string i;
cout << "Are you sad?";
cin >> i;
if (i == "yes" or i == "Yes") {
cout << "\nHello, sad, I'm dad\n";
return(0);
}
else if (i == "no" or i == "No") {
cout << "Good for you pal\n";
return(0);
}
else {
cout << "Answer properly you overgrown flatworm\n";
}
}
}
You have 2 issues:
In the else case, you are not asking for new user input.
You need to return the result of calling response(i), otherwise the code invokes undefined behavior.
else {
cout << "Answer properly you overgrown flatworm\n";
cin >> i;
return response(i);
};
Alternatively, since you never use the return value from response, you can just remove all the return statements, and make it a void function.
If you insist on using recursion then move the input and the check in the same function response() - that function doesn't need to return int at all. In main you can just call response().
void response()
{
string i;
cout << "Are you sad?";
cin >> i;
if (i == "yes" or i == "Yes")
{
cout << "\nHello, sad, I'm dad\n";
}
else if (i == "no" or i == "No")
{
cout << "Good for you pal\n";
return;
}
else
{
cout << "Answer properly you overgrown flatworm\n";
response();
}
}
int main()
{
response();
}
Im new to programing in a whole and I was wondering how I could loop my while loop. I'm making a calculator and i've gotten to a part where I have the program ask whether or not the user wants to end the program, if the user answers "Yes" the program will end; however I have noticed that if the user answers "No" the program will just keep on working and not ask the question again. Is there a way where I can have it ask the question again?
while (response != "Yes" && response != "No") {
cout << "Would you like to end the program? Yes or No" << endl;
cin >> response;
if (response == "Yes") {
calculator_running = false;
} else if (response == "No") {
calculator_running = true;
} else {
cout << "Please choose a valid response" << endl;
}
}
Best practice is to split code to smaller pieces to keep concerns separated.
bool promptYesNo(const std::string& reason)
{
std::cin.clear(); // clear any error flags on cin
std::cout << reason << "\nType \"Yes\" or \"No\": ";
std::string answear;
while (std::cin >> answear) {
if (answear == "Yes") return true;
if (answear == "No") return false;
std::cout << "Please select \"Yes\" or \"No\": ";
}
// here standard input has ended, so terminating application:
std::exit(1);
}
while (!promptYesNo("Would you like to end the program?")) {
...
}
Note that std::cin.clear(); will protect you from invalid state of std::cin. Most probably this is source of your problems. For example some part of program was reading int value, but you have provided a letters. This setts error flags on cin and any later reads will fail.
You need to put the calculator_running to be checked in the while-part of the loopo, something like this:
calculator_running = true;
while (calculator_running)
...
Lke this, once you enter "Yes", that variable will be put to false, and you'll jump out of the loop.
The main trick with while-loops is that you always need to set the condition to true, just before you start the while-loop.
I have noticed that if the user answers "No" the program will just keep on working and not ask the question again
That's what you told the program to do!
If you don't want an entry of "No" to end the loop, take it out of the condition:
while (response != "Yes") {
Or, use your boolean, which is a bit "cleaner" (but ultimately has the same effect):
while (calculator_running) {
so that's my first time learning a language , and I was really excited to play with classes, i do have one major problem which i cant seem to understand ;
im building a bank menu . its a class ofcourse, and i have a different class of clients which is basicly an array in my bank.
so my menu function inside the bank looks like that :
void menu (){
manager();
int n,m;
cout << "welcome to our bank managment system. \n";
cout << "please choose one of the following options : \n";
cout << "1-add a new client\n";
cout << "2-remove a leaving client\n";
cout << "3-bank statistics\n";
cout << "4-if you are a costumer\n";
cout << "5-exit\n";
cin >> n ;
if()
if()
if()
if()
if()
note that my return function is been summoned a lot inside
i have a return function to go back to the menu :
void returnfunction (){
int back = 0;
cout << "\n0-back to menu \n press a diffrent number back to exit :)\n";
cin >> back ;
if (back==0){
return menu();
}
if (back!=0){
cout << "thank you for using our program ! ";
return ;
}
it is working perfect until i play with it to much , and then hit 5 to exit (that's my option for n==5)
i must emphasize that when im hitting 5 only after few simple actions its working fine...
how can i fix that return function ?
ofcourse my main looks like that :
int main()
{
srand (time(NULL));
Bank b ;
b.menu();
}
appricate all of your wisom , thanks a lot
Your function:
void returnfunction ()
is declared to return nothing (void) but you:
return menu();
do return something, that's very unclear (even though menu() returns void too)
If you want to call menu() and then return write:
menu();
return;
There are a couple problems with this code, and honestly it wouldn't compile in other imperative OO languages. But this is c++ where the rules don't matter. Aside: If you don't have a strong reason to be using C++, learn Rust first. I promise you'll thank me later.
Paul has the right of it. The compiler should error out at that statement:
return menu();
However the equivalent is perfectly legal:
menu();
return;
But this still will cause problems in theory (but maybe not in practice) because your function is almost, but not, a candidate for tail recursion optimisation. More here Which, if any, C++ compilers do tail-recursion optimization?
This becomes a problem when users return to the menu many times, it depletes your programs memory, eventually leading to a stack overflow fault. The common pattern you'll find in most every GUI / Graphics library is that of a main-loop. Something like:
int main() {
bool exit = false
while(!exit) {
int action = menu()
switch(action) {
case EXIT_SELECTION: exit = true; break;
case SHOW_STATISTICS: printStats(); break;
}
}
}
Each time you call a function, your program has to use more memory to keep track of everything related to that function call. Ordinarily this memory is released once a function ends, but because your menu function calls another function that calls your menu function that calls another function... and on and on, you will eventually run out of memory from all of the function calls since these functions cannot terminate until the functions they call terminates -- and thus your program will crash. The solution is to use a while loop and check the user's input for an exit code as a previous responder mentioned. It can look something like:
`void menu() {
char choice= '\0';
while(choice!= 3) {
std::cout << "Welcome to the menu!";
std::cout << "\t Option 1 \n";
std::cout << "\t Option 2 \n";
std::cout << "\t Option 3 \n";
std::cout << "Your option: ";
std::cin >> choice;
if(choice == 1) { /*do something*/ }
else if(choice == 2) { /*do something else*/ }
else if(choice == 3) { /*print a message and exit*/ }
else { /*bad choice -- try again*/ }
} //end while-loop
} //end menu()`
Also, notice that your functions' return types are void, which, by definition, cannot have any sort of return. C++ will allow you to say return; inside of a void function, but it is merely a way to escape the function right then and there and is not really intended to do anything more than that. Using return in any other way when working with void functions is confusing and runs a risk of causing big issues.
Just started C++ at university so I've decided to try and do a classic 'shop project' using C++.
I'm just wandering if there is any way to repeat an if statement from the else. For example in the code I'm asking the user if the would like help browsing the shop and if they reply yes then it shows them the options, if they reply no then it continues with the code, however if it isn't yes or no then the code tells the user it can't understand the user.
What I am asking is can I get the user to input the value again and it re-run the if statement without using a loop, or do I have to use a loop?
Here's the bit of code:
cin >> help;
if (help == "Yes" || help == "yes")
{
cout << "These are out current sections that you are able to browse:" << endl;
cout << "-Offers and Deals (1) \n-Computing (2) \n-Console (3) \n-Audio (4) \n-Electronic Displays (5) \n-Cabling (6) \n-General Accessories (7)" << endl;
}
else if (help == "No" || help == "no")
{
cout << "You have chosen not to look at our browsing list." << endl;
}
else
{
cout << "Sorry the system does not understand what you have entered. \n Please use full English (Yes/No)." << endl;
}
If anyone could help me with this, that would be great.
I know its simple code and probably a lot more efficient ways of doing it, just using the ways ive currently been taught at university so far.
Thank in advance.
without using a loop, or do I have to use a loop?
There are ways of achieving this without using a loop, but a loop is exactly the construct that allows you to repeat a block of code while a condition is true.
This explicitly expresses your intent and achieves the result you desire.
void menu()
{
while (true)
{
int i; std::cin >> i;
if (i == 0) { action0(); }
else if (i == 1) { action1(); }
else if (i == 2) { break; /* Return to caller */ }
else { /* Invalid selection, retry */ }
}
}
Another reasonable solution uses recursion. Example:
void menu()
{
int i; std::cin >> i;
if (i == 0) { action0(); }
else if (i == 1) { action1(); }
else if (i == 2) { return; /* Return to caller */ }
else { menu(); /* Invalid selection, retry */ }
}
However, this can have several drawbacks compared to a loop. Quoting François Andrieux from the comments:
Recursion is a reasonable alternative if loops can't be used, but it's worth mentioning the drawbacks. For example, there may be a limit to the number of times you can recur if tail call optimization doesn't come into play. If it does, then you essentially have a hidden loop. It's also worth mentioning that you can't do this with main. It's a mistake many beginners make when they first discover recursion.
void InputStatisticalData()
{
//variables declaration
cout << "\n[Here to take in data]" << endl;
//cin data
while (exit == false)
{
cout << "Entered Loop" << endl;//for troubleshooting purpose
cout << "CountCheck: " << countcheck << endl;//for troubleshooting purpose
if (!Vector.empty())
{
cout << "Entered Vector check IF" << endl;//for troubleshooting purpose
if (condition)//checks if data has any duplicates
{
cout << "\nData already exist, please enter a new set of data." << endl;
break;
}
else
{
cout << "Entered countcheck++" << endl;//for troubleshooting purpose
countcheck++;
}
}
else
{
//stores data
exit = true;
}
}
}
Hi guys, above is my function to take in some data and store them into an object before storing into a vector. Everything works fine, therefore i decided to do some validation checking for the function. 1 of it is to check if the data keyed in, is it already been keyed in before.
I can store the data once and that's it, once i attempt to store it again, it will go into an infinite loop and give me a segmentation fault. I have been trying to solve it for a week but to no avail.
Another infinite loop is the cin.fail. It goes into an infinite loop as well if a wrong input is detected.
Thanks for taking your time to take a look.
Lol, why keep down-voting my questions, there's a question and a solution, it's suppose to help others, so stop down-voting and upvote it
You are dealing with an infinite loop because the error flags are not reset at the end of your iterations.
You should do a cin.clear() to reset the failbit before attempting any other operations:
if(cin.fail())
{
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); //skip bad input
...
}
On your second loop, you check if the vector of data is empty or not. If it is not empty (a second entry) and your data is new, it will fall indefinitely in the else statement that increases countcheck.
Two things may happen: an infinite loop or a segmentation fault (out of bounds exception).
You should check for an upperbound limit, e.g.:
if(countcheck > Vector.size())
{
//This data is new
PTD.setLD(LD);
Vector.push_back (PTD);
cout << "\nRecord stored successfully, returning back to main menu." << endl;
exit = true;
}
else if(Vector[countcheck].getX() == MainX &&
...
}
You could also use a for statement instead:
for(countcheck = 0; countcheck < Vector.size(); countcheck ++)
{
if(Vector[countcheck].getX() == MainX && ...)
{
...
exit = true;
break;
}
}
//New element
if(countercheck == Vector.size())
{
PTD.setLD(LD);
Vector.push_back (PTD);
cout << "\nRecord stored successfully, returning back to main menu." << endl;
exit = true;
}