10
\$\begingroup\$

I have been working on the code listed here.
I am working on Problem #2, 0 stars. Here's what I have to do:

Write a program that presents the user w/ a choice of your 5 favorite beverages (Coke, Water, Sprite, ... , Whatever). Then allow the user to choose a beverage by entering a number 1-5. Output which beverage they chose.

NOTE: I won't be creating a switch statement or making a error message if you give a value other than one that is 1-5, because that are part of the next step in the project.

using std::cout;
using std::cin;
using std::endl;


int main()
{
    int UserSodaChoice;

    cout << "Cola Machine V1" << endl;
    cout << "1. Coca-Cola" << endl;
    cout << "2. Sprite" << endl;
    cout << "3. Fanta" << endl;
    cout << "4. Mountain Dew" << endl;
    cout << "5. Pepsi" << endl;
    cout << "Pick your favorite beverage: ";
    cin >> UserSodaChoice;

    if (UserSodaChoice == 1)
    {
        cout << "I don't like Coca-Cola...";
    }
    if (UserSodaChoice == 2)
    {
        cout << "Sprite is ok I guess.";
    }
    if (UserSodaChoice == 3)
    {
        cout << "Waayyyy too much sugar.";
    }
    if (UserSodaChoice == 4)
    {
        cout << "Mountain Dew! Nice.";
    }
    if (UserSodaChoice == 5)
    {
        cout << "Eww. People don't drink Pepsi these days, it ain't the 90's.";
    }
    cout << "\n\n\n\t\t\t";
}
\$\endgroup\$
  • \$\begingroup\$ This is minor enough not to warrant being an answer, but: convention dictates that variables use camelCase or snake_case, while PascalCase is reserved for type definitions (classes, etc.). \$\endgroup\$ – Schism Oct 21 '14 at 16:13
  • \$\begingroup\$ @Schism really? Our professor told us to use the Hungarian Case Method for EVERYTHING. \$\endgroup\$ – Chantola Oct 21 '14 at 16:15
  • \$\begingroup\$ As far as I'm aware, Hungarian Convention is something entirely different -- it's the practice of prefixing variable names with a string indicating its type. (Examples: strHelloWorld, bIsRaining, btnCancel, $this) \$\endgroup\$ – Schism Oct 21 '14 at 16:18
  • \$\begingroup\$ @Schism -_- Just another thing to add to the list of "put on professor's desk anonymously after we get our grades in". Okay, then what typing convention is it? \$\endgroup\$ – Chantola Oct 21 '14 at 16:24
  • \$\begingroup\$ By convention, I meant your naming; since UserSodaChoice is a variable rather than a class or a struct, it should be userSodaChoice or similar instead. (You'll notice that the highlighting here will even change!) \$\endgroup\$ – Schism Oct 21 '14 at 17:44
11
\$\begingroup\$

Good for you, avoiding using namespace std;.

Within the constraints you requested (i.e. no switch block, no error handling), there's not that much to review.

Avoid excessive use of endl, as that flushes the output unnecessarily. You could write the menu as one long constant. (String literals placed immediately after each other will be treated as one.) Flush it just once at the end, using std::flush. As @LokiAstari points out, you don't need to explicitly flush at all; reading from cin will automatically cause a flush of cout.

cout << "Cola Machine V1\n"
        "1. Coca-Cola\n"
        "2. Sprite\n"
        "3. Fanta\n"
        "4. Mountain Dew\n"
        "5. Pepsi\n"
        "Pick your favorite beverage: ";
\$\endgroup\$
  • \$\begingroup\$ Flush the output? \$\endgroup\$ – user33739 Oct 21 '14 at 1:53
  • \$\begingroup\$ What do you mean by flushing the output? \$\endgroup\$ – Chantola Oct 21 '14 at 1:54
  • 4
    \$\begingroup\$ @piperchester Since writing to the screen involves operating system-level code, which has high overhead, it's more efficient to do so in larger batches. Therefore, writing to cout actually puts characters into a buffer. Sometimes the buffer contents are automatically transferred to screen, in a process known as "flushing". You can also force a flush to happen wherever you need to ensure that the user sees everything you have tried to write so far. \$\endgroup\$ – 200_success Oct 21 '14 at 3:28
  • 2
    \$\begingroup\$ No point in flushing std::cout. When ever you read from std::cin the std::cout is automatically flushed. There is very little reason to ever force a flush. \$\endgroup\$ – Martin York Oct 21 '14 at 4:58
  • \$\begingroup\$ @LokiAstari one reason is that then you can modify the code, move the cin around, and not have to worry about a hanging cout. Also, it makes your intentions explicit. \$\endgroup\$ – Davidmh Oct 22 '14 at 6:41
12
\$\begingroup\$

I would build a closely-coupled mapping between the item and the message, which makes it easier to add, remove, and reorder items without things going screwy. For a really simple example:

struct Beverage {
    const std::string name;
    const std::string message;
    Beverage(const std::string& name, const std::string& message):
        name(name),
        message(message)
    {}
};

int main() {
    Beverage choices[5] = {
        Beverage("Coca-Cola", "I don't like Coke"),
        Beverage("Sprite", "Too much sugar"),
        // ...
    };

    for (int i = 0; i < 5; i++) {
        std::cout << i + 1 << ": " << choices[i].name << std::endl;
    }

    int selected;
    std::cin >> selected;
    if (selected >= 1 && selected <= 5) {
        std::cout << choices[selected - 1].message << std::endl;
    } else {
        std::cout << "Invalid choice" << std::endl;
    }

    return 0;
}

Obviously this is not the overall program structure, but an example of how to make a closer coupling between the choices and responses.

\$\endgroup\$
12
\$\begingroup\$

Yet another way of performing the dispatch according to a user selection would be with an array of strings. Or better still, with an std::array, assuming your compiler is C++11 capable (which is probably the case):

// Add these somewhere:
#include <array>
#include <string>

...

const std::array<std::string, 5> choices = {
    "I don't like Coca-Cola...",
    "Sprite is ok I guess.",
    "Waayyyy too much sugar.",
    "Mountain Dew! Nice.",
    "Eww. People don't drink Pepsi these days, it ain't the 90's."
};

if (UserSodaChoice >= 1 && UserSodaChoice <= choices.size())
{
    cout << choices[UserSodaChoice - 1];
}
// Else, handle invalid input...

Notice the - 1 when indexing the array. This is because UserSodaChoice ranges from 1 to 5, while the array range is 0 to N-1.

\$\endgroup\$
  • \$\begingroup\$ Wouldn't a str::string[5] achieve the same? (I don't know C++). \$\endgroup\$ – Mephy Oct 21 '14 at 1:57
  • 2
    \$\begingroup\$ @Mephy - Yes, absolutely. I suggested std::array just to make some advertisement of the new library ;). It has some advantages over a raw array though. One of the nicest things is that std::array knows its length, something that a string[] would lack (you would have to keep some external constant or variable with the length, or remember it in your head). There are other benefits to it too. Take a look at the link I've provided, it has a few examples. \$\endgroup\$ – glampert Oct 21 '14 at 2:11
  • 1
    \$\begingroup\$ std::array<> is fine. Personally I would have preferred std::vector<> as there is no magic numbers involved (5). I would probably have used UserSodaChoice <= choices.size() as now adding an element to the array means three edits to the code. \$\endgroup\$ – Martin York Oct 21 '14 at 17:16
  • \$\begingroup\$ @LokiAstari - You are right, UserSodaChoice <= choices.size() would be more adequate. I should change that. As for the vector, I think you said yourself in a review somewhere not to use a dynamic allocation where a static will do. If the OP wants to upgrade to a vector in the future, the transition with array is seamless. No need to pay for the dynamic allocation if you don't need one. \$\endgroup\$ – glampert Oct 21 '14 at 17:26
  • \$\begingroup\$ Pay is a rather loaded word. Here the cost is one allocation for the life of the object. But in real life I would not reject this in a code review. So its fine. I just wanted to state my preference for an vector. \$\endgroup\$ – Martin York Oct 21 '14 at 18:26
10
\$\begingroup\$

Just something minor:

I would suggest using a switch statement instead of multiple if statements.

switch(UserSodaChoice){

case 1:
    cout << "I don't like Coca-Cola...";
    break;
case 2:
    cout << "Sprite is ok I guess.";
    break;
case 3:
    cout << "Waayyyy too much sugar.";
    break;
case 4:
    cout << "Mountain Dew! Nice.";
    break;
case 5:
    cout << "Eww. People don't drink Pepsi these days, it ain't the 90's.";
    break;
default:
    cout << "Error.\n";
    break;
}
cout << "\n\n\n\t\t\t";

NOTE: I won't be creating a switch statement or making a error message...

Oops, sorry. For some reason I did not see this. I guess that would make my review useless to you, but I don't think it is completely moot.

\$\endgroup\$
  • 1
    \$\begingroup\$ Using if()else or switch is usually a mistake. There is usually a better data driven solution. \$\endgroup\$ – Martin York Oct 21 '14 at 5:04
2
\$\begingroup\$

Using C++11

// some includes you can figure out
...
// good choice for adding to the global name space.
using std::cout;
using std::cin;
using std::endl;

// Use predefined data structures (STL) when possible
// they are usually less error prone and sometimes faster.
using Products = std::pair<std::string, std::string>;

// Don't use magic numbers like 5 or use some const to describe them.
// Use resizeable arrays like vector unless the size is set in stone, which
// vending machines aren't, adding Coke-Lite gives fewer problems this way.
std::vector<Products> brands {
    { "Coca-Cola", "I don't like Coca-Cola..." },
    { "Sprite", "Sprite is ok I guess." },
    { "Fanta", "Waayyyy too much sugar." },
    { "Mountain Dew", "Mountain Dew! Nice." },
    { "Pepsi", "Eww. People don't drink Pepsi these days, it ain't the 90's." }
};

void DisplayMenu() {
    int count = 0;

    cout << "Cola Machine V1" << "\n";
    for(auto prod: brands)
        cout << ++count << ". " << prod.first << "\n";

    cout << "Pick your favorite beverage: ";
}

int Vending() {
    // always initialize your variables,
    // even if you ATM can see they don't need it
    int UserSodaChoice = 0;

    DisplayMenu();  // reduce code clutter by using functions

    cin >> UserSodaChoice;

    // ToDo in V2 validate UserSodaChoice
    auto prod = brands[UserSodaChoice-1];
    cout << prod.first << ": " << prod.second << endl;

    return UserSodaChoice;
}

int main() {
    return Vending(); // do something fantastic with the return value
}

If you are learning to use classes later then you can easily transform it into a class as the structure is already there.

\$\endgroup\$
0
\$\begingroup\$

Flush your output.. And use if else... Instead of using just and if statement.. Using if else.. Reduces the execution time of your program... NOTE: using a structure further simplifies your program.

Also use a do while loop..

do
{ 
    //Print ur menu... 
    //Ask the user if he wants to select again..
}
while ( condition) 

This will make the program look professional.. Also include a clear screen (clrscr()) within the do while..

\$\endgroup\$
  • \$\begingroup\$ I've edited your post; you can make code blocks appear by indenting code by 4 spaces. You can also inline code with backticks ```. When you edit you should see edit and formatting help to your right. \$\endgroup\$ – Pimgd Oct 22 '14 at 10:04
  • \$\begingroup\$ Since when is using a do-while going to make your program look professional? \$\endgroup\$ – Chantola Oct 26 '14 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.