C++ Primer Plus Exercises and Answers - Chapter 16

Exercises are selected from: C++ Primer Plus (Sixth Edition)
The content is for reference only, please correct me if there is any mistake!

Smart pointer template class

review questions

1. Consider the following class declaration:

class RQ1
{
  private:
    char * st; // points to C-style string
  public:
    RQ1() { st = new char [1]; strcpy(st,""); }
    RQ1(const char * s)
    {st = new char [strlen(s) + 1]; strcpy(st, s); }
    RQ1(const RQ1 & rq)
    {st = new char [strlen(rq.st) + 1]; strcpy(st, rq.st); }
    ~RQ1() {delete [] st};
    RQ & operator=(const RQ & rq);
    // more stuff
};

Convert it to a declaration using a string object. Which methods no longer need to be explicitly defined?

class RQ1
{
  private:
    string st; 
  public:
    RQ1():st("") {}
    RQ1(const char * s):st(s){}
    ~RQ1() {};
    // more stuff
};

Because the string object provides its own memory management functionality, there is no need to explicitly define a copy constructor, destructor, and assignment operator.

2. Point out at least two advantages of string objects over C-style strings in terms of ease of use.

The string object has its own memory management function, so you don't need to worry about the problem that the string exceeds the storage capacity, and you can assign a string object to another string object.

3. Write a function that takes a string object as an argument and converts the string object to all uppercase.

#include<string>
#include<cctype>
void ToUpper(std::string& s)
{
    for(int i = 0; i < s.size(); ++i)
        str[i] = toupper(str[i];)
}

4. Conceptually or syntactically, which of the following is not the correct way to use auto_ptr (assuming the required header files have been included)?

auto_ptr<int> pia(new int[20]);
auto_ptr<string> (new string);
int rigue = 7;
auto_ptr<int>pr(&rigue);
auto_ptr dbl (new double);
auto_ptr<int> pia(new int[20]);  //Error, new should be used instead of new[]
auto_ptr<string> (new string);   //error, pointer not named
int rigue = 7;                   
auto_ptr<int>pr(&rigue);         //Error, cannot point to automatic variables, because rigue is not new
auto_ptr dbl (new double);       //Error, missing <double>, expected auto_ptr<double> dbl(new double);

5. Why is it (conceptually) a bad golf bag if you can generate a stack that stores golf clubs (instead of numbers)?

The last-in-first-out (LIFO) nature of the stack means that when the target stick is retrieved, all sticks that were pocketed (pushed) after the target stick need to be deleted.

6. Why is the set container a bad choice for recording golf scores hole by hole?

The set collection only stores one copy of each value, that is, it has a deduplication mechanism, so multiple identical scores will be stored as one score.

7. Since a pointer is an iterator, why didn't the STL designers simply use pointers instead of iterators?

The access method of the iterator is to abstract the access logic of different collections, so that the effect of looping through the collections can be achieved without exposing the internal structure of the collections. Using an iterator enables traversing data that is not organized in an array, such as data in a doubly linked list, using an object whose interface resembles a pointer.

8. Why did the STL designers only define an iterator base class, but use inheritance to derive classes of other iterator types, and express algorithms in terms of these iterator classes?

STL methods make it possible to use STL functions for regular pointers to regular arrays as well as iterators to STL container classes, thus increasing generality.

9. Give 3 examples where vector objects are more convenient than regular arrays.

A vector object can be assigned to another vector object; vector can manage its own memory and automatically adjust the length; vector can use the at() method to automatically check the boundary.

10. If the program Listing 16.9 were implemented using list s (instead of vector s), what parts of the program would be illegal? Can the illegal part be easily fixed? If so, how to fix it?

List does not support random access, so the public sort method cannot be used, and the sort method of list itself needs to be used. The list does not support random scrambling, you can put it into the vector, then use the vector to scramble, and then switch back to the list.

11. Assuming there is a function symbol TooBig shown in Listing 16.15, what is the function of the following code? What value is assigned to bo?

bool bo = TooBig<int>(10)(15);

TooBig is defined as follows:

template <class T>
class TooBig {
private:
    T cutoff;
    
public:
    TooBig(const T& t):cutoff(t){}
    bool operator()(const T& v){return v > cutoff; }
};

For TooBig<int>(10)(15), T is int, 10 is used to initialize cutoff, 15 corresponds to v in operator()(const T& v), then bo = TooBig<int>(10)( 15) = 15 > 10 = true.

programming exercise

1. A palindrome refers to a string that reads the same both forward and reverse. For example, "tot" and "otto" are both short palindromes. Write a program that lets the user enter a string, and passes the string reference to a bool function. The function will return true if the string is a palindrome, false otherwise. At this point, don't worry about complex issues like capitalization, spacing, and punctuation. i.e. this simple version will reject "Otto" and "Madam, I'm Adam". See the list of string methods in Appendix F to simplify this task.

According to the definition of a palindrome, we can easily think of it: flip a string and compare whether the flipped string is the same as the original string to determine whether the string is a palindrome. The specific implementation is as follows:

#include <iostream>
#include <string>

bool palindrome(const std::string& s);

int main() {
    std::string input;
    std::cout << "Enter a word(q to quit): ";

    while (std::getline(std::cin, input) && input != "q") {
        if (palindrome(input))
            std::cout << input << " is palindrome!" << std::endl;
        else
            std::cout << input << "is not palindrome" << std::endl;
        std::cout << "Enter anthor word(q to quit): ";
    }

    std::cout << "Bye!~" << std::endl;

    return 0;
}

bool palindrome(const std::string& s) {
    std::string rev(s.rbegin(), s.rend());
    return (rev == s);
}

2. The same problems as given in Programming Exercise 1, but consider complex issues such as capitalization, spaces, and punctuation. That is, "Madam, I'm Adam" will be tested as a palindrome. For example, a test function might shorten the string to "madamimadam" and then test whether the reverse is the same. Don't forget about the useful cctype library, from which you may find several STL functions useful, although you don't have to use them.

main.cpp:

#include <iostream>
#include <string>
#include <cctype>

bool palindromePlus(const std::string& s);

int main() {
  std::string input;
  std::cout << "Enter a word(q to quit): ";

  while (std::getline(std::cin, input) && input != "q") {
    if (palindromePlus(input))
      std::cout << input << " is palindrome!" << std::endl;
    else
      std::cout << input << "is not palindrome" << std::endl;
    std::cout << "Enter anthor word(q to quit): ";
  }

  std::cout << "Bye!~" << std::endl;

  return 0;
}

bool palindromePlus(const std::string& s) {
  // data preprocessing
  std::string target_s;
  for (auto& c : s) {
    if (isalpha(c)) {
      if (isupper(c))
        target_s.push_back(tolower(c));
      else
        target_s.push_back(c);
    }
  }
  std::string rev(target_s.rbegin(), target_s.rend());
  return (rev == target_s);
}

3. Modify Listing 16.3 so that it reads words from a file. One solution is to use vector<string> objects instead of string arrays. This allows you to use push_back( ) to copy the words from the data file into a vector<string> object, and use size( ) to determine the length of the word list. Since the program should read from the file one word at a time, the operator >> should be used instead of getline( ). The words contained in the file should be separated by spaces, tabs or newlines.

hangman.cpp:

// hangman.cpp -- some string methods
#include <cctype>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

using std::string;
using std::vector;

vector<string> readWordlist(const std::string &file_name);

int main() {
    using std::cin;
    using std::cout;
    using std::endl;
    using std::tolower;
    std::srand(std::time(0));
    auto wordlist = readWordlist("chapter16-3/wordlist.txt");
    char play;
    cout << "Will you play a word game? <y/n> ";
    cin >> play;
    play = tolower(play);
    while (play == 'y') {
        string target = wordlist[std::rand() % wordlist.size()];
        int length = target.length();
        string attempt(length, '-');
        string badchars;
        int guesses = 6;
        cout << "Guess my secret word. It has " << length
            << " letters, and you guess\n"
            << "one letter at a time. You get " << guesses << " wrong guesses.\n";
        cout << "Your word: " << attempt << endl;

        while (guesses > 0 && attempt != target) {
            char letter;
            cout << "Guess a letter: ";
            cin >> letter;
            if (badchars.find(letter) != string::npos ||
                attempt.find(letter) != string::npos) {
                cout << "You already guessed that. Try again.\n";
                continue;
            }
            int loc = target.find(letter);
            if (loc == string::npos) {
                cout << "Oh, bad guess!\n";
                --guesses;
                badchars += letter;  // add to string
            } else {
                cout << "Good guess!\n";
                attempt[loc] = letter;
                // check if letter appears again
                loc = target.find(letter, loc + 1);
                while (loc != string::npos) {
                    attempt[loc] = letter;
                    loc = target.find(letter, loc + 1);
                }
            }
            cout << "Your word: " << attempt << endl;
            if (attempt != target) {
                if (badchars.length() > 0) cout << "Bad choices: " << badchars << endl;
                cout << guesses << " bad guesses left\n";
            }
        }
        if (guesses > 0)
            cout << "That's right!\n";
        else
            cout << "Sorry, the word is " << target << ".\n";
        cout << "Will you play another? <y/n> ";
        cin >> play;
        play = tolower(play);
    }
    cout << "Bye\n";
    return 0;
}

vector<string> readWordlist(const std::string &file_name) {
    std::ifstream fin;
    fin.open(file_name);
    if (!fin.is_open()) {
        std::cout << file_name << " open fail!" << std::endl;
        std::exit(EXIT_FAILURE);
    }
    vector<string> wordlist;
    string word;
    while (fin >> word) wordlist.emplace_back(word);
    return wordlist;
}

4. Write a function with an old-style interface whose prototype is as follows:

int reduce(long ar[], int n);

The arguments should be the name of the array and the number of elements in the array. This function sorts the array, removes duplicate values, and returns the number of elements in the reduced array. Please write this function using STL functions (if you decide to use the generic unique( ) function, please note that it will return the end of the range of results). Use a small program to test the function.

main.cpp:

#include <algorithm>
#include <iostream>
#include <iterator>

int reduce(long ar[], int n);

int main() {
    long arr[10] = {15, 8, 5, 6, 11, 11, 6, 6, 198, 50};
    int newsize = reduce(arr, 10);
    std::ostream_iterator<long, char> out(std::cout, " ");
    std::copy(arr, arr + newsize, out);
    std::cout << std::endl;
    std::cout << "There are " << newsize << " numbers.";

    return 0;
}

int reduce(long ar[], int n) {
    std::sort(ar, ar + n);
    auto past_end = std::unique(ar, ar + n);

    return past_end - ar;
}

5. The problem is the same as in Programming Exercise 4, but write a template function:

template <class T>
int reduce(T ar[], int n);

Test the function in a small program that uses long instances and string instances.

main.cpp:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>


template <class T>
    int reduce(T ar[], int n);

template <class T>
    void show(T ar[], int n);

int main() {
    long arr[10] = {15, 8, 5, 6, 11, 11, 6, 6, 198, 50};
    int newsize = reduce(arr, 10);
    show(arr, newsize);

    std::string arr_str[] = {"hello", "world", "hello", "hi"};
    newsize = reduce(arr_str, sizeof(arr_str) / sizeof(arr_str[0]));
    show(arr_str, newsize);

    return 0;
}

template <class T>
    int reduce(T ar[], int n) {
    std::sort(ar, ar + n);
    auto past_end = std::unique(ar, ar + n);

    return past_end - ar;
}

template <class T>
    void show(T ar[], int n) {
    std::ostream_iterator<T, char> out(std::cout, " ");
    std::copy(ar, ar + n, out);
    std::cout << std::endl;
    std::cout << "There are " << n << " numbers.";
}

6. Rewrite the example shown in Listing 12.12 to use the STL queue template class instead of the Queue class from Chapter 12.

customer.h:

#ifndef CUSTOMER_H_
#define CUSTOMER_H_

class Customer {
    private:
    long arrive;      // arrival time for customer
    int processtime;  // processing time for customer
    public:
    Customer() { arrive = processtime = 0; }
    void set(long when);
    long when() const { return arrive; }
    int ptime() const { return processtime; }
};

#endif  // CUSTOMER_H_

customer.cpp:

#include "customer.h"

#include <cstdlib>

void Customer::set(long when) {
    processtime = std::rand() % 3 + 1;
    arrive = when;
}

bank.cpp:

// bank.cpp -- using the Queue interface
// compile with queue.cpp
#include <cstdlib>  // for rand() and srand()
#include <ctime>    // for time()
#include <iostream>
#include <queue>

#include "customer.h"

const int MIN_PER_HR = 60;
bool newcustomer(double x);  // is there a new customer?
int main() {
    using std::cin;
    using std::cout;
    using std::endl;
    using std::ios_base;
    // setting things up
    std::srand(std::time(0));  // random initializing of rand()
    cout << "Case Study: Bank of Heather Automatic Teller\n";
    cout << "Enter maximum size of queue: ";
    int qs;
    cin >> qs;
    std::queue<Customer> line;  // line queue holds up to qs people
    cout << "Enter the number of simulation hours: ";
    int hours;  // hours of simulation
    cin >> hours;
    // simulation will run 1 cycle per minute
    long cyclelimit = MIN_PER_HR * hours;  // # of cycles
    cout << "Enter the average number of customers per hour: ";
    double perhour;  // average # of arrival per hour
    cin >> perhour;
    double min_per_cust;  // average time between arrivals
    min_per_cust = MIN_PER_HR / perhour;
    Customer temp;       // new customer data
    long turnaways = 0;  // turned away by full queue
    long customers = 0;  // joined the queue
    long served = 0;     // served during the simulation
    long sum_line = 0;   // cumulative line length
    int wait_time = 0;   // time until autoteller is free
    long line_wait = 0;  // cumulative time in line
    // running the simulation
    for (int cycle = 0; cycle < cyclelimit; cycle++) {
        if (newcustomer(min_per_cust))  // have newcomer
        {
            if (line.size() >= qs)
                turnaways++;
            else {
                customers++;
                temp.set(cycle);     // cycle = time of arrival
                line.emplace(temp);  // add newcomer to line
            }
        }
        if (wait_time <= 0 && !line.empty()) {
            line.pop();                // attend next customer
            wait_time = temp.ptime();  // for wait_time minutes
            line_wait += cycle - temp.when();
            served++;
        }
        if (wait_time > 0) wait_time--;
        sum_line += line.size();
    }
    // reporting results
    if (customers > 0) {
        cout << "customers accepted: " << customers << endl;
        cout << " customers served: " << served << endl;
        cout << " turnaways: " << turnaways << endl;
        cout << "average queue size: ";
        cout.precision(2);
        cout.setf(ios_base::fixed, ios_base::floatfield);
        cout << (double)sum_line / cyclelimit << endl;
        cout << " average wait time: " << (double)line_wait / served
            << " minutes\n";
    } else
        cout << "No customers!\n";
    cout << "Done!\n";
    return 0;
}
// x = average time, in minutes, between customers
// return value is true if customer shows up this minute
bool newcustomer(double x) { return (std::rand() * x / RAND_MAX < 1); }

7. Lottery cards are a common game. On the cards are numbered dots, some of which are chosen at random. Write a lotto( ) function that takes two arguments. The first parameter is the number of dots on the lottery card, and the second parameter is the number of dots randomly selected. The function returns a vector<int> object containing (in sorted order) randomly selected numbers. For example, the function can be used like this:

vector<int> winners;
winners = Lotto(51,6);

This will assign to winner a vector containing 6 randomly selected numbers from 1 to 51. Note that rand( ) alone cannot accomplish this task, as it will generate duplicate values. Hint: Have the function create a vector of all possible values, use random_shuffle( ), and then get the values ​​by the first value of the shuffle vector. Write a small program to test this function.

main.cpp:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

std::vector<int> lotto(int tot_num, int select_num);

int main() {
    const int TOTAL = 51, SELECT_NUMS = 6;
    std::cout << "Play lotto? <y/n>: ";
    std::string choice;
    std::vector<int> winners;

    while (std::cin >> choice && choice != "n") {
        winners = lotto(TOTAL, SELECT_NUMS);
        for (auto it = winners.begin(); it != winners.end(); ++it)
            std::cout << *it << " ";
        std::cout << std::endl;

        std::cout << "Play lotto? <y/n>: ";
    }

    std::cout << "Bye~" << std::endl;
    return 0;
}

std::vector<int> lotto(int total, int select) {
    using std::vector;
    vector<int> all;
    for (int i = 1; i <= total; ++i) all.push_back(i);
    random_shuffle(all.begin(), all.end());

    vector<int> select_vec(all.begin(), all.begin() + select);
    std::sort(select_vec.begin(), select_vec.end());

    return select_vec;
}

8. Mat and Pat want to invite their friends to the party. They have to write a program to complete the following tasks.

  • Ask Mat to enter a list of his friends' names. The names are stored in a container and displayed in sorted order.
  • Have Pat enter a list of her friends' names. The names are stored in another container and then displayed in sorted order.
  • Create a third container, merge the two lists, remove duplicates, and display the contents of this container.

main.cpp:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>

void getNames(std::vector<std::string>& name_vec);
inline void show(std::string& s) { std::cout << s << " "; }

int main() {
    using std::cin;
    using std::cout;
    using std::endl;
    using std::string;
    using std::vector;

    vector<string> mat_friends, pat_friends;
    string name;

    cout << "Mat! Enter your friends(press enter to end): ";
    getNames(mat_friends);

    cout << "Pat! Enter your friends(press enter to end): ";
    getNames(pat_friends);

    std::sort(mat_friends.begin(), mat_friends.end());
    std::sort(pat_friends.begin(), pat_friends.end());

    cout << "Mat's friends: " << endl;
    for_each(mat_friends.begin(), mat_friends.end(), show);
    cout << endl;
    cout << "Pat's friends: " << endl;
    for_each(pat_friends.begin(), pat_friends.end(), show);
    cout << endl;

    // merge to vectors
    vector<string> all_friends;
    all_friends.reserve(mat_friends.size() + pat_friends.size());
    all_friends.insert(all_friends.end(), mat_friends.begin(), mat_friends.end());
    all_friends.insert(all_friends.end(), pat_friends.begin(), pat_friends.end());

    std::sort(all_friends.begin(), all_friends.end());
    auto new_end = std::unique(all_friends.begin(), all_friends.end());
    cout << "All friends: " << endl;
    for_each(all_friends.begin(), new_end, show);
    cout << endl;

    return 0;
}

void getNames(std::vector<std::string>& name_vec) {
    std::string names;
    std::getline(std::cin, names);

    std::istringstream ins(names);
    std::copy(std::istream_iterator<std::string>(ins),
              std::istream_iterator<std::string>(), std::back_inserter(name_vec));
}

9. Compared with arrays, it is easier to add and delete elements in a linked list, but the sorting speed is slower. This leads to the possibility that it might be faster to copy the linked list into an array, sort the array, and then copy the sorted result to the linked list than to sort using the linked list algorithm; more memory. Please use the following method to test the above hypothesis.

a. Create a large vector<int> object vi0 and use rand( ) to provide it with an initial value.

b. Create vector<int> object vi and list<int> object li, both of which have the same length as the initial value and vi0.

c. Calculate the time required to sort vi using the STL algorithm sort( ), and then calculate the time required to sort li using the method sort( ) of list.

d. Reset li to the contents of sorted vi0, and calculate the time required to copy the contents of li into vi, sort vi, and copy the result into li.

To calculate the time required for these operations, use clock( ) from the ctime library. As Listing 5.14 demonstrates, the start time can be obtained using the following statement:

clock_t start = clock();

Then use the following statement to get how much time has passed after the operation:

clock_t end = clock();
cout << (double)(end - start)/CLOCKS_PER_SEC;

This test is not infallible, as the results depend on many factors, such as the amount of memory available, whether multiprocessing is supported, and the length of the array (list) (the efficiency of arrays relative to lists becomes more pronounced as the number of elements to be sorted increases) . Also, use the release build if the compiler provides a default build and a release build. Given the speed of today's computers, obtaining meaningful results may require using as large an array as possible. For example, try 100000, 1000000, and 10000000 elements.

main.cpp:

#include <algorithm>
#include <ctime>
#include <iostream>
#include <list>
#include <random>
#include <vector>

const int SIZE = 1000000;

int main() {
    using std::cin;
    using std::cout;
    using std::endl;
    using std::list;
    using std::vector;

    vector<int> vi0(SIZE, 0);
    std::srand(std::time(0));
    for (int i = 0; i < SIZE; ++i) vi0.at(i) = rand();

    vector<int> vi(vi0);
    list<int> li(SIZE, 0);
    std::copy(vi0.begin(), vi0.end(), li.begin());

    clock_t start = std::clock();
    std::sort(vi.begin(), vi.end());
    clock_t end = clock();
    cout << "sort vector time: " << (double)(end - start) / CLOCKS_PER_SEC
        << endl;

    start = std::clock();
    li.sort();
    end = clock();
    cout << "sort list time: " << (double)(end - start) / CLOCKS_PER_SEC << endl;

    std::copy(vi0.begin(), vi0.end(), li.begin());
    start = std::clock();
    std::copy(li.begin(), li.end(), vi.begin());
    std::sort(vi.begin(), vi.end());
    std::copy(vi.begin(), vi.end(), li.begin());
    end = clock();
    cout << "copy2vec-sort_vec-copy2list time: "
        << (double)(end - start) / CLOCKS_PER_SEC << endl;

    return 0;
}

10. Please modify Listing 16.9 (vect3.cpp) as follows.

a. Add member price to structure Review.

b. Instead of using vector<Review> to store input, use vector<shared_ptr<Review>>. Don't forget that shared_ptr must be initialized with the pointer returned by new.

c. After the input phase, use a loop to let the user choose one of the following ways to display the books: display in original order, display in alphabetical order, display in ascending order by rating, display in descending order by rating, display in ascending order by price, display in descending order by price, quit.

Here's a possible solution: After getting the input, create another shared_ptr vector and initialize it with the original array. Define an operator < ( ) function that compares pointers to structures, and use it to sort the second vector so that the shared_ptr s in it are sorted by the book titles in the objects they point to. Repeat the above process to create a shared_ptr vector sorted by rating and price. Note that by using rbegin() and rend(), the creation of a shared_ptr vector in reverse order is avoided.

vect3.cpp:

// vect3.cpp -- using STL functions
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Review {
    std::string title;
    int rating;
    double price;
};

bool operator<(const std::shared_ptr<Review>& p1,
               const std::shared_ptr<Review>& p2);
bool RatingAsc(const std::shared_ptr<Review>& p1,
               const std::shared_ptr<Review>& p2);
bool PriceAsc(const std::shared_ptr<Review>& p1,
              const std::shared_ptr<Review>& p2);
bool PriceDesc(const std::shared_ptr<Review>& p1,
               const std::shared_ptr<Review>& p2);
bool FillReview(std::shared_ptr<Review>& rptr);
void ShowReview(const std::shared_ptr<Review>& rptr);
void ShowMenu();

int main() {
    using namespace std;

    vector<shared_ptr<Review> > books;

    // initialize books
    shared_ptr<Review> temp_ptr;
    while (FillReview(temp_ptr)) books.push_back(temp_ptr);

    if (books.size() > 0) {
        ShowMenu();
        char choice;
        while (cin >> choice && choice != '6') {
            vector<shared_ptr<Review> > books_cpy(books);
            switch (choice) {
                case '1':
                    cout << "Original order:" << endl;
                    cout << "Rating\tBook\tPrice\n";
                    for_each(books_cpy.begin(), books_cpy.end(), ShowReview);
                    break;
                case '2':
                    cout << "Alphabet order:" << endl;
                    cout << "Rating\tBook\tPrice\n";
                    sort(books_cpy.begin(), books_cpy.end());
                    for_each(books_cpy.begin(), books_cpy.end(), ShowReview);
                    break;
                case '3':
                    cout << "Rating ascending:" << endl;
                    cout << "Rating\tBook\tPrice\n";
                    sort(books_cpy.begin(), books_cpy.end(), RatingAsc);
                    for_each(books_cpy.begin(), books_cpy.end(), ShowReview);
                    break;
                case '4':
                    cout << "Price ascending:" << endl;
                    cout << "Rating\tBook\tPrice\n";
                    sort(books_cpy.begin(), books_cpy.end(), PriceAsc);
                    for_each(books_cpy.begin(), books_cpy.end(), ShowReview);
                    break;
                case '5':
                    cout << "Price descending:" << endl;
                    cout << "Rating\tBook\tPrice\n";
                    sort(books_cpy.begin(), books_cpy.end(), PriceDesc);
                    for_each(books_cpy.begin(), books_cpy.end(), ShowReview);
                    break;
                default:
                    break;
            }
            ShowMenu();
        }
    } else
        cout << "No entries. ";
    cout << "Bye.\n";
    return 0;
}

bool operator<(const std::shared_ptr<Review>& p1,
               const std::shared_ptr<Review>& p2) {
    return p1->title < p2->title;
}

bool RatingAsc(const std::shared_ptr<Review>& p1,
               const std::shared_ptr<Review>& p2) {
    return p1->rating < p2->rating;
}

bool PriceAsc(const std::shared_ptr<Review>& p1,
              const std::shared_ptr<Review>& p2) {
    return p1->price < p2->price;
}

bool PriceDesc(const std::shared_ptr<Review>& p1,
               const std::shared_ptr<Review>& p2) {
    return p1->price > p2->price;
}

bool FillReview(std::shared_ptr<Review>& rptr) {
    rptr = std::shared_ptr<Review>(new Review);

    std::cout << "Enter book title (quit to quit): ";
    std::getline(std::cin, rptr->title);
    if (rptr->title == "quit") return false;

    std::cout << "Enter book rating: ";
    std::cin >> rptr->rating;
    if (!std::cin) return false;

    std::cout << "Enter book price: ";
    std::cin >> rptr->price;
    if (!std::cin) return false;

    // get rid of rest of input line
    while (std::cin.get() != '\n') continue;

    return true;
}

void ShowReview(const std::shared_ptr<Review>& rptr) {
    std::cout << rptr->rating << "\t" << rptr->title << "\t" << rptr->price
        << std::endl;
}

void ShowMenu() {
    using std::cin;
    using std::cout;
    using std::endl;

    cout << "---------------------------------------------------------" << endl;
    cout << "1.original order   2.alphabet order    3.rating ascending" << endl;
    cout << "4.price ascending  3.price descending  6.quit" << endl;
    cout << "---------------------------------------------------------" << endl;
    cout << "Your choice: ";
}

Original link

C++ Primer Plus Exercises and Answers - Chapter 16

Tags: C++ Algorithm programming language

Posted by mike0193 on Tue, 10 Jan 2023 08:07:29 +0300