Using a while loop without breaks or if’s for user input [duplicate]

So in this main.cpp, I have to implement a class’s interface where it reads a groceryitem from standard input until end of file. I am to do this is by storing the object in a dynamically created object, storing the grocery item’s pointer in a standard vector, and trying to prompt for data within the loop’s condition clause. Here is the following code.


    #include <iomanip>
    #include <iostream>
    #include <utility>
    #include <vector>
    
    #include "GroceryItem.hpp"
    
    int main() {
      std::cout << "Welcome to Kroger.  Place grocery items into your shopping cart by entering each item's information. n"
                << "   enclose string entries in quotes, separate fields with comas n"
                << "   for example: "00016000306707", "Betty Crocker", "Double Chocolate Chunk Cookie Mix", 17.19 n"
                << "   Enter CTL-Z (Windows) or CTL-D (Linux) to quit n";
      std::vector<GroceryItem *> shoppingcart;
      do {
        std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
        GroceryItem* Item = new GroceryItem;
        std::cin >> *Item;
        shoppingcart.push_back( Item );
        std::cout << "Item added to shopping cart: " << *shoppingcart.back() << "n";
      } while( !( std::getline( std::cin, *Item ) ) );
      std::cout
          << "Here is an itemized list of the items in your shopping cart:n";
      for (auto i = shoppingcart.begin(); i < shoppingcart.end(); i++) {
        std::cout << **i << 'n';
      }
      for (const auto erase : shoppingcart ) {
        delete erase;
    
      }
      return 0;
    
    }

Aside from the do while statement, everything else does it’s job as needed. Yet, for the while loop, although the pattern is meant to be a loop-and-a-half pattern, i’m not allowed to use any decision statements within the body of the loop (ie, if’s or break’s). Furthermore, i’m not allowed to include fstream or call the ifstream::open function, but I can create a text file with my input and then redirect input from that text file (the only problem is that I don’t know how, considering that every solution I have found uses fstream). Does anybody have an idea of what I could write for this while loop? The current do-while above will not work because of the error below.

    ./main.cpp:21:40: error: use of undeclared identifier 'Item' [Semantic Issue]
       21 |   } while( !( std::getline( std::cin, *Item ) ) );
          |     
    

Also, here is the sample input:

   "00072250018548","Nature's Own","Nature's Own Butter Buns Hotdog - 8 Ct",10.79
   
   "00028000517205", "Nestle"             , 
   "Nestle Media Crema Table Cream"       , 
   17.97
   
   "00034000020706"    , 
   "York", 
   "York Peppermint Patties Dark Chocolate Covered Snack Size", 
   12.64 "00038000570742", 
   "Kellogg's", "Kellogg's Cereal Krave Chocolate",
     18.66
   
   "00014100072331" , "Pepperidge Farm", "Pepperidge Farm Classic Cookie Favorites", 14.43
   
   "00000000000000", "incomplete / invalid item"

Thank you.

New contributor

Cevapi Man69 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

5

Avoid owning pointers, new and delete

Unless the problem specification requires it, you should avoid the use of: (1) owning raw pointers, (2) operator new, and (3) operator delete. By owning, I mean a pointer for which operator delete must be called later.

If, for instance, you are required to use pointers to dynamically allocated GroceryItems, you should use a smart pointer, such as std::unique_ptr.

std::vector<std::unique_ptr<GroceryItem>> shoppingcart;

If, however, the problem specification for your shoppingcart does not explicitly require the use of pointers, then you will be better off just declaring a vector of GroceryItem objects.

std::vector<GroceryItem> shoppingcart;

A loop-and-a-half

This is a bit of a niggle, but without some sort of break-statement or return-statement that causes an exit from within the body of a loop, then, technically, the loop is not a loop-and-a-half.

So, if you cannot use break, then you must use a “normal” loop to simulate the behavior of a “loop-and-a-half.” The way to do that is to repeat half of the loop body right before (or, in some cases, right after) the loop. The repeated code is the “half” part in “loop-and-a-half.”

For the program in the OP, the repeated code goes right before the loop.

std::vector<GroceryItem> shoppingcart;
GroceryItem item;

std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";  // REPEATED
while (std::cin >> item) {
    shoppingcart.emplace_back(std::move(item));
    std::cout << "nItem added to shopping cart: n" << shoppingcart.back() << "n";
    std::cout << "Enter UPC, Product Brand, Product Name, and Pricen"; // REPEATED
}

Here is the same code, but this time using a break-statement. A “normal” loop has the loop-termination test at either the top or bottom of the loop. A “loop-and-a-half” has it in the middle. Either way, the loop has a single entry, and single exit.

std::vector<GroceryItem> shoppingcart;
GroceryItem item;

for (;;) {
    std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
    if (!(std::cin >> item)) 
        break;
    shoppingcart.emplace_back(std::move(item));
    std::cout << "nItem added to shopping cart: n" << shoppingcart.back() << "n";
}

They told me I gotta use pointers!

The code does not look much different when you use smart pointers and dynamically allocated GroceryItems. In part, that’s because you can dereference a smart pointer using the same * operator you use for regular pointers.

std::vector<std::unique_ptr<GroceryItem>> shoppingcart;
GroceryItem item;

std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
while (std::cin >> item) {
    shoppingcart.emplace_back(std::make_unique<GroceryItem>(std::move(item)));
    std::cout << "nItem added to shopping cart: n" << *shoppingcart.back() << "n";
    std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
}

Notes:

  • The program uses operator>> to extract a GroceryItem, and store it in “local” variable item.
  • The item is then converted into an rvalue (by std::move). This ravlue becomes the argument to std::make_unique.
  • std::make_unique allocates a GroceryItem on the heap, and returns a std::unique_ptr that points to it.
  • Because the argument to std::make_unique is an rvalue std::make_unique will use the move constructor from class GroceryItem when it initializes the GroceryItem it has allocated on the heap. This requires, of course, that a move constructor exists. If not, std::make_unique will use the copy constructor.

There is a big advantage in using smart pointers. You never have to call operator delete on them. The destructor of a smart pointer does that for you.

My boss insists: it’s gotta be raw pointers!

It is not as easy as you might think to adapt this for use with raw pointers. The following may be what a teacher expects on a homework assignment, but it is buggy code.

// WARNING: This leaks memory!
std::vector<GroceryItem*> shoppingcart;  // "raw" pointers
GroceryItem item;

std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
while (std::cin >> item) {
    shoppingcart.emplace_back(new GroceryItem{ std::move(item) });  // may throw
    std::cout << "nItem added to shopping cart: n" << *shoppingcart.back() << "n";
    std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
}

// More code ...

// Delete pointers
for (auto& p : shoppingcart) {
    delete p;
    p = nullptr;
}

This may look okay, because there is a loop at the end that deletes all the pointers.

It’s buggy, however, because it has the potential to leak memory. That will happen when the vector is not empty, and a call to operator new throws a std::bad_alloc. It will also happen when the vector is not empty, and an exception of any kind is thrown.

In order to prevent these sorts of memory leaks, you have to build an RAII class around the vector—the class might be named ShoppingCart—and move the deletion loop into the destructor for that class. The destructor is guaranteed to be called, even when an exception is thrown, so no matter what happens, all the pointers will be deleted.

Stream extraction operator

These examples use the stream extraction operator>> that was developed for your last question. It follows the example from the answer I posted there.

std::vector<GroceryItem *> shoppingcart;

You should store things by-value unless there’s a good reason not to, so change the line above to:

std::vector<GroceryItem> shoppingcart;

i’m not allowed to use any decision statements within the body of the loop (ie, if’s or break’s).

You want the success of parsing a GroceryItem to control the loop though, so should move the parsing attempt down to the while condition:

  do {
    std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
    shoppingcart.emplace_back(); // add a default-constructed GroceryItem
  } while (std::cin >> shoppingcart.back() &&
           std::cout << "Item added to shopping cart: "
                     << shoppingcart.back() << 'n');
  shoppingcart.pop_back(); // remove last one we didn't parse into successfully

The above is a bit scrappy, but if the GroceryItem class implements operator>> properly for parsing from streams, then we need a GroceryItem ready to overwrite with the items successively read from input. We could add a default-constructed GroceryItem to the vector each time, and overwrite it in place, which I’ve illustrated above. Note the use of the short-circuit logical-AND operator && in the while condition: that means execution only invokes the std::cout << ... part if the std::cin >> part indicates success.

An alternative is to create one before the loop and keep blatting over that:

  GroceryItem gi;
  do {
    std::cout << "Enter UPC, Product Brand, Product Name, and Pricen";
  } while (std::cin >> gi &&
           (shoppingcart.push_back(std::move(gi)),
            std::cout << "Item added to shopping cart: "
                      << shoppingcart.back() << 'n'));

Here, we only push_back if std::cin >> gi succeeds, but because push_back doesn’t return anything to indicate success, we ignore its return value and add the std::cout statements return value to the condition using the comma operator: basically, (first_expression, second_expression) runs first_expression, then second_expression, but only the latter’s return value is substituted into the containing expression.

To invoke the program on a file, on Linux you can use either of:

./my_program < my_input_file
cat my_input_file | ./my_program

On Windows, try:

.my_program < my_input_file

NOTE: for the above to work, GroceryItem must have a working operator>> that copes properly with the quoted strings, comma separators, newline delimiters, and the shorter invalid-grocery-item input. Writing that is quite fiddly for beginners. Hopefully your teacher’s provided something already. If not, you’ll want to use code such as is >> std::quoted(groceryitem.upc) >> sep && sep == ',' && ... to read the quoted strings, then after reading two parts check for the invalid item and decide whether to continue reading the product name and price.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật