-1

So I wanted to revise part of my program. This involved me creating another accumulator in the buyCoffee function. The program works flawlessly, except only 1 of my 3 main function variables being passed by reference is being updated. If you run the code, buy coffee in all 3 sizes, and then check cupsSold, Small is the only one being updated in main(). I'm very confused since the code for each coffee size is identical. There's probably some underlying interactions I am not aware of which is causing this.

// This program acts like a coffee shop, with multiple commands made for the manager, and one for the cashier. You can buy coffee(cashier), see how many sizes were sold for the day(manager), see how many ounces of coffee were sold for the day(manager), and view revenue for the day(manager). You can quit to close the shop.


#include <iostream>
#include <iomanip>
#include <string>
using namespace std;


//Global variables for prices and ounces.
double largeCoffee = 2, mediumCoffee = 1.90, smallCoffee = 1.75;
int smlOz = 9, medOz = 12, lrgOz = 15;


//Function initialization.
void showMainMenu();
void buyCoffee(double &totSmall, double &totMedium, double &totLarge);
void cupsSold(double totalSmall, double totalMedium, double totalLarge);
void coffeeSold(double totalSmall, double totalMedium, double totalLarge, int smlOz, int medOz, int lrgOz);
void totalRevenue(double totalSmall, double totalMedium, double totalLarge, double largeCoffee, double mediumCoffee, double smallCoffee);
void printReceipt(int &am1, int &am2, int &am3);


//Main handles the users choice, with if-else statements, after mainmenu has been called.
int main() {
  
  //Variable declaration.
  double totalSmall = 0, totalMedium = 0, totalLarge = 0;
  double& totSmall = totalSmall, totMedium = totalMedium, totLarge = totalLarge;
  string choice;

  //Loop will run until user enters "quit" on the main menu, closing the shop. 
  bool quit = true;
  while (quit == true){
    
    //Main menu function is always called before receiving input in choice.
    showMainMenu();
    cin >> choice;

    //Calls buyCoffee function if the choice is buyCoffee. Clears the variable 'choice' after the function has been run, taking you back the to main menu.
    if (choice == "buyCoffee"){
      //Passes the total coffee cup sizes by reference, so the program can run multiple times and retain and update values until the shop is closed.
      buyCoffee(totSmall, totMedium, totLarge);
      cin.clear();
    }
      
    //Calls cupsSold function if the choice is cupsSold. Clears the variable 'choice' after the function has been run, taking you back to the main menu. Displays the cup sizes sold for the day.
    else if (choice == "cupsSold"){
      //Passes the coffee cup sizes sold by current values.
      cupsSold(totalSmall, totalMedium, totalLarge);
      cin.clear();
    }
    //Calls coffeeSold function if the choice is coffeeSold. Clears the variable 'choice' after the function has been run, taking you back to the main menu.
    else if (choice == "coffeeSold"){
      //Passes the total coffee cup sizes sold by current values, and passes the global oz variables by value. Calculates the coffee oz sold for the day.
      coffeeSold(totalSmall, totalMedium, totalLarge, smlOz, medOz, lrgOz);
      cin.clear();
    }
      //Calls totalRevenue function if the choice is revenue. Clears the variable 'choice' after the function has been run, taking you back to main menu.
    else if (choice == "revenue"){
      //Passes the total cup sizes sold, and the price of each size of coffee to calculate the total revenue made for the day. 
      totalRevenue(totalSmall, totalMedium, totalLarge, largeCoffee, mediumCoffee, smallCoffee);
    }
      
    //Quits the program if the choice is 'quit'. Closes the shop.
    else if (choice == "quit"){
      cout << "The coffee shop is now closed. Run the program to open it again. Thank you!";
      //Exits the main menu loop since quit is now false.
      quit = false; 
    }
      
    //Will reject input if it's unknown. 
    else{
      cout << endl <<"Please input a valid command." << endl << endl;
    }  
  } 
}

//When the program starts, it shows the main menu, created in this function. Output is formatted with parameters left and right justified to view the commands easily on output.
void showMainMenu(){
  
  //The command output is left justified with 15 characters, and the description is right justified by 43 characters.
  cout << endl << setw(15) << left << "COMMAND";
  cout << setw(43) << right << "DESCRIPTION" << endl;
  cout << "==========================================================" << endl;

  cout << setw(15) << left << "buyCoffee";
  cout << setw(43) << right << "Buy coffee, and view the menu." << endl << endl;

  cout << setw(15) << left << "cupsSold";
  cout << setw(43) << right << "Coffee sizes sold for the day." << endl << endl;

  cout << setw(15) << left << "coffeeSold";
  cout << setw(43) << right << "View ounces of coffee sold for the day." << endl << endl;

  cout << setw(15) << left << "revenue";
  cout << setw(43) << right << "View revenue for the day." << endl << endl;

  cout << setw(15) << left << "quit";
  cout << setw(43) << right << "Close the shop for the day." << endl;

  cout << "==========================================================" << endl;
  //Command prompt.
  cout << "Enter Command: ";
}


//*totsmall, *totmedium, and *totallarge reference the cup sizes defined in main().
void buyCoffee(double &totSmall, double &totMedium, double &totLarge){
  //Variables initialized so the user can enter how many coffees they want to purchase.
  int numSmall = 0, numMedium = 0, numLarge = 0;
  string size;
  //Enters another loop for the coffee menu, until user exits.
  int amount1 = 0, amount2 = 0, amount3 = 0;
  int& am1 = amount1, am2 = amount2, am3 = amount3;
  bool quit2 = true;
  while(quit2 == true){
    //Displays menu with the coffees justified on the left 15 characters, as well as prices and ounces of the coffee on the right, justified by 15 characters.
    cout << endl << "MENU" << endl;
    cout << "==============================" << endl;
    cout << setw(15) << left << "Small Coffee";
    cout << setw(15) << right << "9oz - 1.75$" << endl << endl;
      
    cout << setw(15) << left << "Medium Coffee";
    cout << setw(15) << right << "12oz - 1.90$" << endl << endl;

    cout << setw(15) << left << "Large Coffee";
    cout << setw(15) << right << "15oz - 2.00$" << endl;
    cout << "==============================" << endl;

    //User prompt.
    cout << "Enter small, medium, or large. Enter quit to view your receipt (if you purchased anything) and/or return to the main menu: ";
    cin >> size;
    cout << endl;
    
    //If the size is small, it enters another loop for how many the user would like to purchase. 
    if (size == "small"){
      bool quit3 = true;
      while (quit3 == true){
        
        //User prompt.
        cout << "How many would you like to purchase? ";
        cin >> numSmall;
        cout << endl;
        
        //if input is a positive number, it'll add it to the total cup count in main() and call printReceipt function to display the receipt for the customer. If not a positive number, it asks again for correct input until it's correct. After printing the receipt, it'll set quit3 to false to exit the purchase number loop. This code repeats for all 3 sizes.
        if (numSmall > 0){
          
          totSmall += numSmall;
          am1 += numSmall;
          cout << "Anything else?";
          cin.clear();
          quit3 = false;
        }
        else if (numSmall != 0){
          cout << "Invalid input." << endl;
          cin.clear();
          cin.get();
        }
        else{
          cout << "Invalid input." << endl;
          cin.clear();
          cin.get();
        }
      }
    }
      
    //Another loop for the medium size. All three size loops work exactly the same.
    else if (size == "medium"){
      bool quit3 = true;
      while (quit3 == true){
        cout << "How many would you like to purchase? ";
        cin >> numMedium;
        cout << endl;
        if (numMedium > 0){
          totMedium += numMedium;
          am2 += numMedium;
          cout << "Anything else?";
          cin.clear();
          quit3 = false;
        }
        else if (numMedium != 0){
          cout << "Invalid input." << endl;
          cin.clear();
          cin.get();
        }
        else{
          cout << "Invalid input." << endl;
          cin.clear();
          cin.get();
        }
      }
    }
      
    //Another loop for the large size. All three size loops work exactly the same.
    else if (size == "large"){
      bool quit3 = true;
      while (quit3 == true){
        cout << "How many would you like to purchase? ";
        cin >> numLarge;
        cout << endl;
        if (numLarge > 0){
          totLarge += numLarge;
          am3 += numLarge;
          cout << "Anything else?";
          cin.clear();
          quit3 = false;
        }
        else if (numLarge != 0){
          cout << "Invalid input." << endl;
          cin.clear();
          cin.get();
        }
        else{
          cout << "Invalid input." << endl;
          cin.clear();
          cin.get();
        }
      }
    }
    else if (size == "quit"){
      if (numLarge > 0 || numMedium > 0 || numSmall > 0){
        printReceipt(am1, am2, am3);
        cin.clear();
        quit2 = false;
      }
      else{
        cin.clear();
        quit2 = false;
      }
    }
      
    //Safety for invalid input.
    else{
      cout << "Invalid input.";
      cin.clear();
      cout << endl;
      }
    }
}

//Prints the receipt after purchasing coffee. Passes the the size purchased to calculate the total cost for the receipt.
void printReceipt(int &am1, int &am2, int &am3){
  double orderTotal = 0;
  
  //Prints receipt for the large coffee bought.

  orderTotal += (am3 * largeCoffee);
  
  //Again, formats the output like the rest of the program. Same for the next 2 receipts.
  cout << "RECEIPT" << endl;
  cout << "==============================" << endl;
  cout << am3 << " Large Coffee";
  cout << setw(15) << right << "$2.00/each.";
  cout << endl;

  
//Prints receipt for the medium coffee bought. Same as the first one.

  orderTotal += (am2 * mediumCoffee);
  cout <<endl;
  cout << am2 << " Medium Coffee";
  cout << setw(15) << right << "$1.90/each.";
  cout << endl;


  
//Prints receipt for the small coffee bought. Same as the first one.

  orderTotal += (am1 * smallCoffee);
  cout << endl;
  cout << am1 << " Small Coffee";
  cout << setw(15) << right << "$1.75/each." << endl << endl;
  cout << "Order Total: ";
  cout << fixed << setprecision(2) << setw(12) << right << "$" << orderTotal << endl << endl;
  cout << "Thank you, please come again!" << endl;
  cout << "==============================" << endl << endl;

}


//Shows the cups sold for the day, passing the total cup sizes by value.
void cupsSold(double totalSmall, double totalMedium, double totalLarge){
  
  //Formatting like the rest of the program.
  cout << endl << "COFFEE SIZES SOLD TODAY:" << endl;
  cout << "========================" << endl;
  cout << setw(8) << left << "Small";
  cout << fixed << setprecision(0) << setw(16) << right << totalSmall << endl << endl;

  cout << setw(8) << left << "Medium";
  cout <<  fixed << setprecision(0) << setw(16) << right << totalMedium << endl << endl;

  cout << setw(8) << left << "Large";
  cout <<  fixed << setprecision(0) << setw(16) << right << totalLarge << endl;

  cout << "========================" << endl;
  }

//Calculates coffee sold in oz for the day. Passes the cup sizes sold, as well as the size oz amounts by value.
void coffeeSold(double totalSmall, double totalMedium, double totalLarge, int smlOz, int medOz, int lrgOz){
  int ozTotal = (totalSmall * smlOz) + (totalMedium * medOz) + (totalLarge * lrgOz);
  
  //Formatting like the rest of the program.
  cout << endl << "COFFEE OUNCES SOLD TODAY:" << endl;
  cout << "========================" << endl;
  
  cout << setw(16) << left << "Today's Total:";
  cout << fixed << setprecision(0) << setw(8) << right << ozTotal << endl;

  cout << "========================" << endl;
}

//Calculates the revenue (coffee sold) for the day and displays it.
void totalRevenue(double totalSmall, double totalMedium, double totalLarge, double largeCoffee, double mediumCoffee, double smallCoffee){

  double totalRev = (totalSmall * smallCoffee) + (totalMedium * mediumCoffee) + (totalLarge * largeCoffee);
  
  //Formatting like the rest of the program.
  cout << endl << "TOTAL REVENUE TODAY" << endl;
  cout << "=============================" << endl;
  
  cout << setw(16) << left << "Today's Revenue:";
  cout << fixed << setprecision(2) << setw(8) << right << "$" << totalRev << endl;

  cout << "=============================" << endl;
  
}
cigien
  • 57,834
  • 11
  • 73
  • 112
  • 2
    `double& totSmall = totalSmall, totMedium = totalMedium` etc. isn't doing what you think it is. Reference-to and pointer-to attach to variables in that context, not types. – WhozCraig Feb 28 '22 at 01:34
  • You have a lot of _shadowing_ in your code too. Turn on more warnings. If you use `g++` or `clang++` to compile, add `-Wall -Wextra -Werror -pedantic-errors -g -fsanitize=address,undefined` - that usually gives help when both compiling and to find runtime problems. For `g++` you can also add `-fanalyzer` to get some static analysis of the code. – Ted Lyngmo Feb 28 '22 at 01:35
  • You don't need to create additional reference variables to pass them to your function. Just pass `totalSmall, totalMedium, totalLarge`. – Retired Ninja Feb 28 '22 at 01:38
  • @WhozCraig I'm not sure what that means. Could you elaborate a little more? I thought we have to assign the "alias" so to speak to the variable we would want passed by reference? – Darkdrium Feb 28 '22 at 01:42
  • @TedLyngmo I'm currently using Replit. I don't know how to add those to mine. I'm not sure if I'm allowed to use those things, either. What does shadowing mean exactly? But yes, I'm expecting my code to be absolute garbage honestly, but it was a fun project to work on. – Darkdrium Feb 28 '22 at 01:45
  • @Darkdrium Shadowing means that you have multiple variables with the same name. If you have a global variable `foo` and then define a function `bar(int foo);` the two `foo`s are separate entities. Code like that often has bugs where one thinks that it's one variable that is changed, but it's really another. – Ted Lyngmo Feb 28 '22 at 01:48
  • It means, given `int a,b;` there is a difference between `int& aref=a,bref=b;` and `int &aref=a, &bref=b;` The fomer declares *one* reference-to (aref), and just another regular double (bref). The latter declares *two* reference-to ids. It is one of the reasons seasoned programmers will nearly-never declare reference-to or pointer-to variables on the same line in a comma separated list. For pointers, at least, the compiler will usually catch the issue (because a `int*` and `int` are not compatible). But for references it is much easier to do wrong, and much harder to spot. – WhozCraig Feb 28 '22 at 01:50
  • @WhozCraig Thanks!! I appreciate the explanation and help. – Darkdrium Feb 28 '22 at 02:34
  • @TedLyngmo Wait, so since I declared those two global variables, I don't even have to pass them through the functions, correct? I don't know why I didn't logically think that way in the first place. Nor did my teacher point it out after grading. Duhhhh. Thanks so much! Cleaning the code up now. – Darkdrium Feb 28 '22 at 02:42
  • @Darkdrium Exactly - but prefer to have as few global variables as possible. Declare them in main and pass them as parameters to the functions that needs them instead. – Ted Lyngmo Feb 28 '22 at 03:38
  • 1
    @TedLyngmo Understood. I only used two global variables because my instructor used them for her program instructions. Thanks for the help :) – Darkdrium Feb 28 '22 at 14:14

1 Answers1

0

The issue is that when you declare more than one pointer or reference on the same line the * or & binds to the variable, not the type.

This creates a reference for the first variable but the second is just a double.

double& a = something, b = somethingElse;

This creates two references.

double& a = something, &b = somethingElse;

A good way to avoid this issue is to always declare variables one per line. I feel this also helps to make your code more readable.

double& a = something;
double& b = somethingElse;

Based on that I modified your program to something much smaller to focus on the issue and also explain that you don't need to create those references at all.

#include <iostream>
#include <string>

void buyCoffee(double& totSmall, double& totMedium, double& totLarge) 
{
    totSmall = 5;
    totMedium = 10;
    totLarge = 15;
}

void print(const std::string& header, double totalSmall, double totalMedium,
           double totalLarge)
{
    std::cout << header << "\n";
    std::cout << "totalSmall = " << totalSmall
        << ", totalMedium = " << totalMedium
        << ", totalLarge = " << totalLarge << "\n";
}

int main() 
{
    // This uses explicit references as you had in your example with
    // a correction to the second and third variables
    {
        double totalSmall = 0, totalMedium = 0, totalLarge = 0;

        print("Before: ", totalSmall, totalMedium, totalLarge);

        double &totSmall = totalSmall, &totMedium = totalMedium, &totLarge = totalLarge;
        buyCoffee(totSmall, totMedium, totLarge);

        print("After: ", totalSmall, totalMedium, totalLarge);
    }

    // This just passes the variables to the function
    // Since the function accepts them by reference it modifies
    // the original variable inside the function
    {
        double totalSmall = 0, totalMedium = 0, totalLarge = 0;

        print("Before: ", totalSmall, totalMedium, totalLarge);

        buyCoffee(totalSmall, totalMedium, totalLarge);

        print("After: ", totalSmall, totalMedium, totalLarge);
    }
}

If you run this you'll see that both give the same result, the original variables are modified by the function.

Example @ ideone

Output:

Before: 
totalSmall = 0, totalMedium = 0, totalLarge = 0
After: 
totalSmall = 5, totalMedium = 10, totalLarge = 15
Before: 
totalSmall = 0, totalMedium = 0, totalLarge = 0
After: 
totalSmall = 5, totalMedium = 10, totalLarge = 15
Retired Ninja
  • 4,785
  • 3
  • 25
  • 35
  • 1
    Thank you! It's been a slow learning process but doing this simple program has taught me a lot, and you guys have given me a ton of good information. I appreciate it. It's working now. – Darkdrium Feb 28 '22 at 14:20