Reputation: 13
This program was just a fun project I thought I'd work on to practice with C++, all it does is calculate the price of a road trip.
All of this coding was typed and compiled using CODE::BLOCKS, it finished with no errors yet when launching the program, the if statements react as if the first if statement listed withing a function is true no matter what I enter. I'm sure the coding is correct yet I feel I may be missing something.
main.cpp
#include <iostream>
#include "Answers.h"
using namespace std;
int main()
{
Answers roadTrip;
roadTrip.intro();
return 0;
}
Answers.h
#ifndef ANSWERS_H
#define ANSWERS_H
#include <iostream>
using namespace std;
class Answers
{
public:
Answers();
int intro();
void dataInput();
void doubleChecking();
void incDataInput();
void incDataMpg();
void incDataGasPrices();
void incDataDistance();
int goodbye();
int finalGoodbye();
int calculations();
int mpg;
float gasPrices;
int distance;
string answer;
string incValue;
protected:
private:
};
#endif // ANSWERS_H
Answers.cpp
#include "Answers.h"
#include <iostream>
using namespace std;
Answers::Answers(){
}
Answers::intro(){
cout << "Hello and welcome to the trip calculator, how may I be of service?" << endl;
cout << "As of now,I am only able to EXIT from this program, or calculate a simple road trip" << endl;
cout << "Which would you like to do, exit, or calculate?: ";
cin >> answer; cout << "\n" << endl;
if(answer=="Calculate"||"calculate"||"Road trip"||"road trip"){
dataInput();
}
if(answer=="Exit"||"exit"||"Escape"||"escape"||"Quit"||"quit"){
return 0;
}
}
void Answers::dataInput(){
cout << "How many miles to the gallon does your car get?: ";
cin >> mpg; cout << "\n";
cout << "What are the average gas prices along your trip?: ";
cin >> gasPrices; cout << "\n";
cout << "How far away in miles is your destination?: ";
cin >> distance; cout << "\n" << endl;
doubleChecking();
}
void Answers::doubleChecking(){
cout << "Alright, so if I'm not mistaken:" << endl;
cout << "Your car gets " << mpg << " miles per gallon" << endl;
cout << "The average price of gas along the trip is $" << gasPrices << endl;
cout << "And your destination is approximately " << distance << " miles away\n" << endl;
cout << "Is this information correct?: ";
cin >> answer; cout << "\n" << endl;
if(answer=="Yes"||"yes"||"Y"||"y"){
calculations();
}
if(answer=="No"||"no"||"N"||"n"){
incDataInput();
}
}
void Answers::incDataInput(){
cout << "Oh? Well that wont be a problem" << endl;
cout << "So what information was entered incorrectly?" << endl;
cout << "Was it MPG, gas prices, or distance?: ";
cin >> answer; cout << "\n" << endl;
if(answer=="MPG"||"mpg"||"Miles per gallon"||"miles per gallon"){
incDataMpg();
}
if(answer=="Gas"||"gas"||"Gas prices"||"gas prices"){
incDataGasPrices();
}
if(answer=="Distance"||"distance"){
incDataDistance();
}
}
void Answers::incDataMpg(){
cout << "So tell me again, how many miles to the gallon does your car get?: ";
cin >> mpg; cout << "\n";
cout << "Your car gets " << mpg << " miles per gallon, is that correct?: ";
cin >> answer; cout << "\n" << endl;
if(answer=="Yes"||"yes"||"Y"||"y"){
doubleChecking();
}
if(answer=="No"||"no"||"N"||"n"){
incDataMpg();
}
}
void Answers::incDataGasPrices(){
cout << "So tell me again, what's the average price of gas along your road trip?: ";
cin >> gasPrices; cout << "\n";
cout << "The average price of gas along the trip is $" << gasPrices << ", is that correct?: ";
cin >> answer; cout << "\n" << endl;
if(answer=="Yes"||"yes"||"Y"||"y"){
doubleChecking();
}
if(answer=="No"||"no"||"N"||"n"){
incDataGasPrices();
}
}
void Answers::incDataDistance(){
cout << "So tell me again, approximately how many miles away is your destination?: ";
cin >> distance; cout << "\n";
cout << "Your destination is approximately " << distance << " miles away, is that correct?: ";
cin >> answer; cout << "\n" << endl;
if(answer=="Yes"||"yes"||"Y"||"y"){
doubleChecking();
}
if(answer=="No"||"no"||"N"||"n"){
incDataDistance();
}
}
Answers::calculations(){
int extraMoney = distance*.05;
int total = (distance/mpg)*gasPrices;
cout << "So according to my calculations, in order for your car to travel the " << distance << " miles needed" << endl;
cout << "It'd be smart to have at least $" << total << "for your overall road trip, but if I were you, I'd consider" << endl;
cout << "making it $" << total+extraMoney << "Leaving you " << extraMoney << " extra dollars to spend on food an necessities for the trip" << endl;
cout << "I think it should cover " << distance << " miles"; cout << "\n" << endl;
goodbye();
}
Answers::goodbye(){
cout << "Well, hasn't this been fun? We should do it again sometime. I hope I was able to help you out" << endl;
cout << "That being said, would you like me exit this program?: ";
cin >> answer;
if("Yes"||"yes"||"Y"||"y"){
return 0;
}
if("No"||"no"||"N"||"n"){
finalGoodbye();
}
}
Answers::finalGoodbye(){
cout << "Listen, it's nice that you wanna keep me around and all, but literally all I can do is calculate road trips" << endl;
cout << "... and quit out of myself" << endl;
cout << "That being said, would you like me exit this program?: ";
cin >> answer;
if("Yes"||"yes"||"Y"||"y"){
return 0;
}
if("No"||"no"||"N"||"n"){
finalGoodbye();
}
}
Any help would be GREATLY appreciated, I've had the issue before yet I'm still not sure how to fix it.
Upvotes: 1
Views: 1219
Reputation: 145269
First, although the code compiles with the g++ compiler invoked with no options, it doesn't compile with Visual C++, and it doesn't compile with g++ when one uses option -f-no-ms-extensions
to disable language extensions allegedly (once) used in Microsoft code:
[C:\my\forums\so\169] > doskey /macros g++=g++ -std=c++14 -pedantic-errors -Wall -Wextra -fexec-charset=cp1252 $* [C:\my\forums\so\169] > g++ main.cpp Answers.cpp -fno-ms-extensions Answers.cpp:12:16: error: ISO C++ forbids declaration of 'intro' with no type [-fpermissive] Answers::intro() { ^ Answers.cpp:137:23: error: ISO C++ forbids declaration of 'calculations' with no type [-fpermissive] Answers::calculations() { ^ Answers.cpp: In member function 'int Answers::calculations()': Answers.cpp:147:1: warning: no return statement in function returning non-void [-Wreturn-type] } ^ Answers.cpp: At global scope: Answers.cpp:149:18: error: ISO C++ forbids declaration of 'goodbye' with no type [-fpermissive] Answers::goodbye() { ^ Answers.cpp:163:23: error: ISO C++ forbids declaration of 'finalGoodbye' with no type [-fpermissive] Answers::finalGoodbye() { ^ Answers.cpp: In member function 'int Answers::intro()': Answers.cpp:26:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ [C:\my\forums\so\169] > _
The errors above, use of so called “implicit int” syntax from early C, are easily fixed, and I leave that as an exercise for you.
In addition, in order to compile this with other compilers you need to change the include directive in Answers.h
from <iostream>
to <string>
.
With the code fixed so that all errors and all warning diagnostics have been removed, a clean compile, there remains the question of why e.g.
if(answer=="Calculate"||"calculate"||"Road trip"||"road trip") {
dataInput();
}
… always executes dataInput()
, regardless of the value of answer
.
Well, the C++ operators are designed to support expressions like this:
if( x1 != x2 || y1 != y2 ) // The points are different
So the relational operators like !=
and ==
are evaluated before the boolean operators like ||
. I.e. the relational operators have effectively higher precedence. You can find precedence tables for the C++ operators just about everywhere, including at cppreference.com.
Unlike with Python there are no special rules for chains of operators, so the answer
checking code is interpreted as
if( ((((answer=="Calculate") || "calculate") || "Road trip") || "road trip") ) {
dataInput();
}
This compiles because a simple string literal decays to pointer to first char
in any context where that's possible, and it's possible here because a pointer decays further – an implicit conversion – to boolean false
and true
.
A nullpointer produces false
, and any other pointer produces true
.
And since the pointers from the literals are not nullpointers, the above is equivalent to
if( ((((answer=="Calculate") || true) || true) || true) ) {
dataInput();
}
And now you can see how that's completely independent of the value of answer
.
Realizing that this is intended to check whether a given string is in a given set of strings, one can write a function called e.g. contains
, and use it like this:
if( contains( answer, Strings{ "Calculate", "calculate", "Road trip", "road trip" } ) )
{
// Whatever.
}
Defining such a function is simple:
#include <string>
#include <unordered_set>
using namespace std;
using Strings = unordered_set<string>;
auto contains( string const& item, Strings const& set )
-> bool
{ return (set.count( item ) > 0); }
To put this or similar code in a header file, for reuse:
using namespace std;
it's within your namespace and not directly in the global namespace; andinline
to the function.Also, in order to reduce the number of differently cased strings substantially, consider uppercasing or lowercasing the whole answer
string, and in general, the string being checked. For example, check whether the set includes "CALCULATE"
instead of checking each of "Calculate"
, "calculate"
and other possible variations.
A more direct fix is to express each intended comparison directly, like this:
if( answer=="Calculate" ||
answer == "calculate" ||
answer == "Road trip" ||
answer == "road trip"
)
{
// Whatever.
}
But that is awkward, verbose, and doesn't scale.
Upvotes: 0
Reputation: 180201
if statements react as if the first if statement listed withing a function [sic; for example, this one:
if(answer=="Calculate"||"calculate"||"Road trip"||"road trip"){
] is true no matter what I enter. I'm sure the coding is correct yet I feel I may be missing something.
So which is it? Is the code correct, or not?
In fact, the code is not correct for what you appear to intend. You appear to want this:
if(answer=="Calculate"||answer=="calculate"||answer=="Road trip"||answer=="road trip"){
Your original asks whether answer=="Calculate"
is true (which might or might not be the case) or "calculate"
is true (which is always the case) or two other conditions which also are always true.
Upvotes: 0
Reputation: 409176
Expressions like something == somethingelse || somethingthird || ...
doesn't do what you think it does, because of operator precedence your comparison looks like this to the compiler
(answer=="Calculate")||"calculate"||"Road trip"||"road trip"
In other words it compares answer
to the string "Calculate"
, and the uses that result for the logical or operator with the literal string constant, and a literal string constant will be a non-null pointer and so always true, which means the comparison will always be true.
You instead need to do e.g.
answer=="Calculate" || answer=="calculate" || answer=="Road trip" || answer=="road trip"
Upvotes: 2
Reputation: 21542
answer=="Yes"||"yes"||"Y"||"y"
doesn't do what you think it does. Basically, it will always return true because in your "or" clauses you're just putting in const char *
values that are always going to be considered true.
In order for this logic to work properly you have to replace it with answer=="Yes"||answer=="yes"||answer=="Y"||answer=="y"
. Better yet, toupper(answer.c_str()[0])=='Y'
. The same applies to your other string-comparison if
statements.
Upvotes: 0
Reputation: 1168
It has to be if(answer=="MPG"||answer=="mpg"||answer=="Miles per gallon"||answer=="miles per gallon")
and for all the other ifs, too. Otherwise you check that the pointer to "mpg" and all the other string pointers is unequal to 0. And thus the condition is always evaluated as true.
Upvotes: 0