我想用C ++建立咖啡馆。我希望用户使用是或否回答每个问题。如果他们回答是或否,我想显示其总价。

#include <iostream>
using namespace std;
int main()
{
    //Develop a billing statement for a store/restaurant 
    // Define variables 
    double donut, bagel, burrito, sandwhich, omelet, coffee, cappachino, smootie, water, spirit, total, tax;
    char answerType;
    bagel = 2.50;
    burrito = 3.50;
    donut = 1.00;
    sandwhich = 3.50;
    omelet = 1.25;
    coffee = 1.50;
    cappachino = 2.00;
    smootie = 3.25;
    water = 0.99;
    spirit = 1.00;
    total = 0.00;
    tax = 6.25;
    cout << "Welcome to Junelle's Cafe" << endl;
    // Introduction to my cafe

    //Customer knows the options before hand 
    //Customer enters yes (y) or no (n) 
    cout << "Enter a y (yes) or no (n) for every question asked." << endl;

//Ask user if they want a bagel 
    cout << "Would you like to buy a bagel for .50?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A bagel has been added to your order. Your total is " << total + bagel << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item2
    cout << "Would you like to buy a donut for .00?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A donut has been added to your order. Your total is " << total + donut+bagel << endl;

    }


    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item3 
    cout << "Would you like to buy a omelet for .25?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A omelet has been added to your order. Your total is " << total + donut + bagel+ omelet << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item 4
    cout << "Would you like to buy a burrito for .50?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A burrito has been added to your order. Your total is " << total + donut + bagel
            + omelet + burrito << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }

//item 5 
    cout << "Would you like to buy a sandwhich for .50?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A sandwhich has been added to your order. Your total is " << total + donut + bagel
            + omelet+ burrito+sandwhich << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item 6
    cout << "Would you like to buy a coffee for .50?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A coffee has been added to your order. Your total is " << total + donut + bagel 
            + omelet+ burrito + sandwhich + coffee<< endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }

//item 7 
    cout << "Would you like to buy a cappachino for .00?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A cappachino has been added to your order. Your total is " << total + donut + bagel 
            + omelet + burrito + sandwhich + coffee+ cappachino << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item 8
    cout << "Would you like to buy a water for q4312078q.99?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A water has been added to your order. Your total is " << total + donut + bagel
            + omelet + burrito + sandwhich + coffee + cappachino + water << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item 9 
    cout << "Would you like to buy a spirit for .00?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A spirit has been added to your order. Your total is " << total + donut + bagel
            + omelet + burrito + sandwhich + coffee + cappachino + water + spirit<< endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }
//item 10 
    cout << "Would you like to buy a smootie for .25?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A smootie has been added to your order. Your total is " << total + donut + bagel 
            + omelet + burrito + sandwhich + coffee + cappachino + water + spirit + smootie << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }

    cout << "Thank you for visiting Junelle's Cafe" << endl;
    while (1);
    return 0;
}


评论

哦,我记得几个月前我的C ++代码是这样的。继续练习,然后您将编写出更好的代码。问候。

#1 楼

别。重复。你自己。

这是基本原则。您手动反复重复“是否要XY”循环。这不仅容易出错,而且难以维护。例如,假设您要询问客户他们想要多少个物品。然后,您将必须为每个项目添加或更改输入处理。

也许您想使用

 15 bagels
200 coffee
  1 water
  1 donut


在您当前的程序中这样做会很乏味。我们需要int bagelCount, coffeeCount, waterCount, coconoutCount…,您将获得模式。

因此,如果可能的话,我们应该尝试在单个代码块中以相同的方式处理所有项目。这将带我们进入下一个主题。

重用信息

在程序开始时,您会看到bagel = 2.50。但是,当您问客户是否要购买百吉饼时,您不会利用这些信息来谋取利益:

cout << "Would you like to buy a bagel for .50?:";
                                           ^^^^^^


又一次违反了DRY。如果更改bagel价格,则必须在两个位置进行更改。容易出错。相反,请重用您已经掌握的信息:

std::cout << "Would you like to buy a bagel for $" << bagel << ": ";


顺便说一句,如果变量从不更改其值,则将其声明为const。另外,尝试尽可能使用值初始化变量:

typedef double Price;

const Price bagelPrice = 2.50;
const Price coffeePrice = 1.50;


,但是我们稍后会更改,因此请不要使用它。 >
不信任客户

如果客户输入“ a”而不是“ y”或“ n”会怎样?好吧,您接受了它,什么也不添加。

但是,如果您问顾客“您要百吉饼吗?”他们回答“克苏鲁!”,您可能会要求他们接受您的报价或拒绝。一个小的

bool ask_yes_no() {
    /* exercise */
}


功能可以为您提供帮助。

命名空间

除非您不知道自己在做什么,否则不要这么做。现在不使用using namespace std了。所有其他项目都遵循相同的结构,因此让我们专注于这一项目。我们在这里看到一个图案:


我们说产品的名称和价格
我们问他们是否要购买产品
我们将商品添加到订单并增加总数(此步骤不见了)
我们告诉客户他们的新总数
如果我们还有产品,我们选择下一个产品并继续执行1.
否则,我们告诉客户总数。

因此,让我们重写该对话框:

//Ask user if they want a bagel 
    cout << "Would you like to buy a bagel for .50?:";
    cin >> answerType;

    if (answerType == 'y')

    {

        cout << "A bagel has been added to your order. Your total is " << total + bagel << endl;

    }

    else if (answerType == 'n')

    {
        cout << "Your total is " << total << endl;

    }


现在我们所需要的就是获取item_nameitem_price的方法并遍历所有项目。有几种方法:


我们可以使用关联集合,例如std::map<Name, Price>

我们可以将名称和价格结合在一个Item中,并使用常规集合std::vector<Item>


其他评论使用后者,因此让我们看看前面的一个std::map

std::cout << "Would you like to buy a " << item_name
          << " for $" << item_price << ": ";

if(asks_yes_no()) { // see exercise above
    total += item_price;

    std::cout << "A " << item_name<< " has been added to your order. ";
}

std::cout << "Your total is $" << total << ".";


我们现在可以轻松遍历菜单中的所有项目:

typedef std::string ItemName
typedef double Price;

const std::map<ItemName, Price> menu {
    {"bagel",  2.50 },
    {"coffee", 1.50 },
    {"sugar", 0.02 },
    {"full breakfast", 5.00}
};


我们还可以跟踪该顺序:

// You might not know this kind of loop yet.
// It's basically going through the menu.
// The current pair of item and price is item_and_price,
// the .first-Member is the item, and the .second-Member
// is the price.
//
// The following holds throughout the loop:
//
//    menu[item_and_price.first] == item_and_price.second
for(const auto & item_and_price : menu) {
    const auto & item_name = item_and_price.first;
    const auto & item_price = item_and_price.second;

    std::cout << "Would you like to buy a " << item_name 
              << " for $" << item_price << ": ";

    if(asks_yes_no()) { // see exercise above
        total += item_price;

        std::cout << "A " << item_name << " has been added to your order. ";
    }

    std::cout << "Your total is $" << total << ".";
}


浮点值的小注释

浮点算术是' t准确。而不是double,我们应该使用精确的类型和美分而不是浮点类型和美元,例如

typedef unsigned int Amount;

std::map<ItemName, Amount> order;

/* ... */

total += item_price;

order[item_name] = 1;


对于小型玩具程序,double很好。但是,如果您曾经在代码中处理过真钱,请确保客户,您或政府都没有支付太多或没有得到足够的钱。

可用性

最后但并非最不重要的是,您可以向用户显示菜单,让他们选择要购买的商品,例如:

const Cents bagelPrice = 250;


这样,他们就不会如果他们只想购买20个百吉饼,就不必检查所有产品。

评论


\ $ \ begingroup \ $
关于typedef的说明-使用它非常实用。双重语义通常被很好地理解。如果看到双重消息,我会立即知道发生了什么事以及哪些运算符可用。当我看到价格时,我将不得不阅读文档以了解发生了什么,或者更糟的是,我可能会做一些假设,因为这肯定是长久未签名的?这被std :: vector 的typedef(例如)称为Price放大了。
\ $ \ endgroup \ $
–杰拉德
18-2-5在10:11



\ $ \ begingroup \ $
“除非您不知道自己在做什么,否则不要使用命名空间std”。我只是说完全不要使用它。
\ $ \ endgroup \ $
–巴尔德里克
18-2-5在14:48

\ $ \ begingroup \ $
@Gerard另一个明智的方法是以更具描述性的方式命名变量。 double price_per_hour将具有相同的信息,而无需别名。
\ $ \ endgroup \ $
–Filip Minx
18-2-5在15:29

\ $ \ begingroup \ $
由于四舍五入或精度错误,请注意将浮点数用作货币值。 double可能是c ++的最佳选择之一,但可能需要注意一些注意事项:stackoverflow.com/q/149033/1474939
\ $ \ endgroup \ $
–布赖恩(Brian J)
18-2-5在16:49



\ $ \ begingroup \ $
@Baldrickk在这方面,我正在遵循C ++核心准则。 “使用命名空间std的人应该了解std以及这种风险。”
\ $ \ endgroup \ $
– Zeta
18年2月6日在6:40

#2 楼

这是实现此目标的非常简单的方法,而且非常易于阅读。那很棒!但是您可能已经想到有一种更简单的方法。这里是简化它的一些想法。

使用类

请注意您如何拥有多个具有相同属性的项目。他们都有名字和价格。因此将它们组合成一类是有意义的。一个简单的类可能是这样的:

class InventoryItem {
    public:
        InventoryItem (const std::string& itemName, const double itemPrice);
        ~InventoryItem();

        std::string getName() const;
        double getPrice() const;

    private:
        std::string name;
        double price;
};


该类的构造函数只需将name字段设置为传入的项目名称,并将price字段设置为传入的商品价格:

InventoryItem (const std::string& itemName, const double itemPrice) : 
    name (itemName), 
    price(itemPrice) 
{
}


get函数将只返回值:

std::string getName() const
{
    return name;
}


为此,可以使用一个类,您可以通过执行以下操作来创建对象:

InventoryItem newItem("burrito", 3.50);


使用数组

要对它们执行类似的操作,可以将它们放入array中。 array可容纳多个相同类型的项目。因此,在这种情况下,我们需要arrayInventoryItem。我们可以这样创建一个:

#include <array>

int main()
{
    std::array<InventoryItem, 10>   inventory {
        InventoryItem("bagel", 2.50),
        InventoryItem("burrito",3.50),
        InventoryItem("donut", 1.00),
        InventoryItem("sandwhich", 3.50),
        InventoryItem("omelet", 1.25),
        InventoryItem("coffee", 1.50),
        InventoryItem("cappachino", 2.00),
        InventoryItem("smootie", 3.25),
        InventoryItem("water", 0.99),
        InventoryItem("spirit", 1.00)
    };

}


如果数组中有项目,并且想对每个项目执行相同的操作,则可以在一个数组中对其进行迭代。循环。 array是一种“容器”,因为它可以容纳其他对象。标准模板库提供的所有容器(即std::前缀材料)都提供了名为iterator的对象,使您可以逐步浏览容器中的每个项目。您只需调用container.begin()即可获得指向容器中第一项的迭代器,然后对其进行递增以获取下一项。您可以一直增加它,直到它等于container.end(),然后您就知道没有更多要处理的了。因此,让我们尝试使用上面array的库存商品:

for (std::array<InventoryItem, 10>::iterator nextItem = inventory.begin();
     nextItem != inventory.end();
     nextItem++)
{
    std::cout << "Would you like to buy a " << nextItem->getName();
    std::cout << " for " << nextItem->getPrice() << "?\n";

    std::cin >> answerType;

    if (answerType == 'y')
    {
        std::cout << "A " << nextItem->getName();
        std::cout << " has been added to your order. Your total is ";
        std::cout << total << "\n";
    }
    else
    {
        std::cout << "Your total is " << total << "\n";
    }
}


现在注意到关于total的一些奇怪之处吗?它没有正确更新。我没有将该项目的费用加到总计中。我之所以没有这样做,是因为我想让您看看您在您的版本中是如何做到的。在第一个if语句中,如果用户选择“ y”,则打印出total + bagel。但是请注意,您实际上并没有更新total,仅打印了它。您实际上需要将新价格分配给total。因此,您需要执行以下操作:

total = total + bagel;


或者在我使用array和循环的版本中,它只是:

total = total + nextItem->getPrice();


还请注意,在您的版本中,对于第二个if语句,当用户选择“ y”时,您将打印百吉饼和甜甜圈的价格。但是,如果用户为百吉饼选择“ n”,为甜甜圈选择“ y”怎么办?您将打印错误的价格!

因此,如果您一直跟踪总计,并在每个“ y”选择之后对其进行更新,那么它应该始终具有正确的值,并且您无需手动添加所有其他选择。

您可以在结束版本时结束它:

cout << "Thank you for visiting Junelle's Cafe" << endl;
while (1);
return 0;


感谢您加入我们!
/>

评论


\ $ \ begingroup \ $
std :: pair <>是一个特别糟糕的类,因为它没有提供有关所包含内容的有用信息。我避免使用它,并尽可能多地使用元组,因为它使代码不可读。
\ $ \ endgroup \ $
–user1118321
18年2月5日在6:26

\ $ \ begingroup \ $
让我换一种说法(仍然需要咖啡):InventoryItem是具有私有字段的类而不是具有适当命名字段的结构是否有任何优势?
\ $ \ endgroup \ $
– Zeta
18-2-5在6:30



\ $ \ begingroup \ $
主要是为初学者编写的,我认为默认情况下进入大多数字段公开是一个坏习惯。它鼓励编写难以调试的代码。这样的简单类将来也可能会包含更多字段。我主要打算将其作为一个实际类的外观的基本示例。
\ $ \ endgroup \ $
–user1118321
18年2月5日在6:35

\ $ \ begingroup \ $
我认为您实际上不需要类-std :: map itemPrices同样有意义,可以更快地进行查找,并且所需的代码更少。到目前为止,这还绰绰有余。如果需要更多功能(例如,针对特定商品的折扣),则可以随时重构代码。
\ $ \ endgroup \ $
–莫妮卡基金的诉讼
18-2-5在7:53



\ $ \ begingroup \ $
我建议使用std :: vector而不是std :: array。这样,如果将来更改项目,则无需更改声明。有10个项目的事实不是该程序固有的。
\ $ \ endgroup \ $
– Mark H
18年2月5日在8:06

#3 楼

除了上述答案

永远不要在财务计算中使用浮点数

这是不好的,因为浮点数加法不精确并且舍入误差会在非整数中传播-直觉的方式。进行更复杂的计算时,很可能会失去或获得1美分。

它可能适用于您的简单示例,但请注意,记帐系统通常使用定点表示形式。

由于C ++不支持定点,所以最简单的解决方法是使用整数并将所有价格存储为美分。

评论


\ $ \ begingroup \ $
小注释:这本质上就是我在评论中立即使用typedef double Price的原因。可以轻松地将其更改为适合进行财务计算的类型。
\ $ \ endgroup \ $
– Zeta
18年2月5日在9:48

\ $ \ begingroup \ $
“从不在财务计算中使用浮点数”-我听过很多建议,并且习惯于自己分配。但是后来我也听说银行确实在内部使用浮点数来表示货币,因为在任何情况下,精确度的不足都不会蔓延到整个便士/美分/…。因此,有(合理的)声称这在实践中不是问题。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
18-2-5在12:07



\ $ \ begingroup \ $
@Konrad-我认为建议的完整形式(就像大多数编程建议一样)以“除非您理解并接受所有后果”为结尾。 IME,金融软件作者知道(或应该知道)何时使用精确(定点)算法(例如为交易添加价格),何时使用浮点数(例如,在计算利息支付时)以及何时(关键)何时在两者之间进行转换他们。没什么能说的是,您不能使用美分/便士/전/分/øre/ kopek / ...作为浮点的单位,因此这似乎并不是二分法。
\ $ \ endgroup \ $
– Toby Speight
18-2-5在13:50



\ $ \ begingroup \ $
@TobySpeight:也许使用定点数最重要的一点是,它迫使程序员考虑对每个操作取整。我想这对于计算利息,增加价格等也很重要。当中间结果的精度更高时,很难实现可重复性。
\ $ \ endgroup \ $
– Andreas H.
18-2-5在14:34



\ $ \ begingroup \ $
@MartinYork 0.01是1/100,不是2的幂的和(或者我傻吗?)
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
18-2-5在19:49



#4 楼

已经有一些不错的建议,但是我注意到其中一些使用了STL,您可能会暂时不会学习。您可以通过使用类来整合cafe的功能来大大简化程序。

类似这样的东西:

struct Product
{
    static float total;
    static constexpr float tax = 6.25;
    char desc[16];
    float price;
    bool pitchItem(); 
}productList[10] =
{
    "bagel",2.50,
    "burrito",3.50,
    "donut", 1.00,
    "sandwich", 3.50,
    "omlet", 1.25,
    "coffee", 1.50,
    "cappachino", 2.00,
    "smoothie", 3.25,
    "water", .99,
    "spirit",1.00
};


此产品结构包含您需要的所有信息。静态成员是类的成员,而不是对象的成员,因此它们可以用于存储全局信息或聚合信息。在这种情况下,方法不需要参数,因为它们假定您在引用范围中的desc和price值。在此示例中,所有大小都是静态的。这不是出于节省空间而进行编程的最有效方法,但是任何其他方法都可能会过大,我故意远离STL和动态内存,因为我想您还没有学到。

pitchItem()方法通过执行以下操作来消除多余的输入:

bool Product::pitchItem()
{
    bool goodAnswer = true;
    char answer;
    std::cout << "Would you like to buy a " << desc << "?: ";
    std::cin >> answer;
    if(answer == 'y' || answer == 'Y')
    {
        total += price;
        std::cout << "A "<< desc << " has been added to your order. Your total is $"<< std::fixed << std::setprecision(2) <<total << "\n"; 
    }
    else if(answer == 'n' || answer == 'N')
    {
        std::cout << "Your total is $" << total << "\n";
    }
    else
    {
        std::cout << "Sorry, I must have misunderstood you.\n";
        goodAnswer = false;
    }
    return goodAnswer;
}


执行类似的操作可使您使用for来迭代所有选择循环。返回问题的状态,因此您可以执行以下操作以确保输入有效:

    if(!productList[i].pitchItem())
    {
        i--;
    }


希望有所帮助。