C++ Primier Plus(第六版) 第十六章 string类和标准模板库 编程练习答案

1.回文指的是顺读和逆读都一样的字符串。例如,“tot”和“otto”都是简单的回文。编写一个程序,让用于输入字符串,并将字符串引用传递给一个bool函数。如果字符串是回文,该函数将返回true,否则返回false。此时不要担心诸如大小写、空格和标点符号这些复杂的问题。即这个简单的版本将拒绝“Otto”和“Madam, I'm Adam”。请查看附录F中的字符串方法列表,以简化这项任务。

本题的难点是将字符串的回文返回出来,在判断是否是回文。返回回文的函数编写过程中遇到了问题,最后一个字符应该是.size()-1而不是size()。
程序如下:

// pe1.cpp -- test palindrome
#include <iostream>
#include <string>

std::string palindrome(const std::string & s);
bool is_palindrome(const std::string & s);
int main()
{
    using std::cout;
    using std::string;
    using std::cin;
    string input;
    cout << "Enter a word(quit to quit): ";
    while (cin >> input && input != "quit")
    {
        if (is_palindrome(input))
            cout << input << " is palindrome\n";
        else
            cout << input << " is not palindrome\n";
        cout << "Enter a word(quit to quit): ";
    }
    cout << "Done\n";
    return 0;
}

std::string palindrome(const std::string & s)       // get the palindrome of string s
{
    std::string result;
    int size = s.size();
    result = s;
    char temp;
    for (int i = 0, j = size-1; i < j; i++, j--)    // j should be size - 1
    {
        temp = s[i];
        result[i] = result[j];
        result[j] = temp;
    }
    return result;
}

bool is_palindrome(const std::string & s)
{
    return palindrome(s) == s;
}

运行结果如下:
image

2. 与编程练习1中给出的问题相同,但要考虑诸如大小写、空格和标点符号这样的复杂问题。即“Madam, I'm Adam”将作为回文来测试。例如,测试函数可能会将字符串缩略为“madamimadam”,然后测试倒过来是否一样。不要忘了有用的cctype库,您可能从中找到几个有用的STL函数,尽管不一定非要使用它们。

本题使用练习一的代码,再次基础上增加了预处理函数,该函数通过isalpha()函数创建了notalpha()函数,用来找出非字母的字符,使用remove_if(),将非字母字符删除,删除后需要擦除尾部的区间,删除之后进行小写字母转换,利用自己编写的函数进行,通过调用自己的toLower()函数实现,该函数返回tolower()。代码如下:

// pe2.cpp -- test palindrome
#include <iostream>
#include <cctype>
#include <string>
#include <algorithm>
bool notalpha(char ch) { return !isalpha(ch); }     // is not alpha
char toLower(char ch) { return tolower(ch); }       // ch to lower
std::string ToLower(const std::string & s);                      // string to lower
std::string predeal(const std::string & s);                      // delete not alpha and ToLower
std::string palindrome(const std::string & s);
bool is_palindrome(const std::string & s);
int main()
{
    using std::cout;
    using std::string;
    using std::cin;
    string input;
    cout << "Enter a line of string (quit to quit): ";
    while (getline(cin, input) && input != "quit")
    {
        string deal_s = predeal(input);
        if (is_palindrome(deal_s))
            cout << input << " is palindrome\n";
        else
            cout << input << " is not palindrome\n";
        cout << "Enter a line of string (quit to quit): ";
    }
    cout << "Done\n";
    return 0;
}

std::string ToLower(const std::string & s)
{
    std::string temp;
    for (int i = 0; i < s.size(); i++)
        temp[i] = toLower(s[i]);
    return temp;
}
std::string predeal(const std::string & s)
{
    std::string deal_result;
    deal_result = s;
    auto end_ir = remove_if(deal_result.begin(), deal_result.end(),notalpha);
    deal_result.erase(end_ir, deal_result.end());
    return ToLower(deal_result);
}
std::string palindrome(const std::string & s)       // get the palindrome of string s
{
    std::string result;
    int size = s.size();
    result = s;
    char temp;
    for (int i = 0, j = size-1; i < j; i++, j--)    // j should be size - 1
    {
        temp = s[i];
        result[i] = result[j];
        result[j] = temp;
    }
    return result;
}

bool is_palindrome(const std::string & s)
{
    return palindrome(s) == s;
}

运行结果如下:

image

###3. 修改程序清单 16.3,使之文件从读取单词。一种方案是,使用vector<string>对象而不是string数组。这样便可以使用push_back()将数据文件中的单词复制到vector<string>对象中,并使用size()来确定单词列表的长度。由于程序应该每次从文件中读取一个单词,因此应使用运算符>>而不是getline()。文件中包含的单词应该用空格、制表符或换行符分隔。 本题考查的是从文件读取单词,这里选择将读取的单词存到vector<string>对象中,可以调用对象的push_back()方法,从文件读取单词,首先声明一个ifstream对象inFile,通过open方法将文件与对象关联,通过inFile >> word,读取单词,可以是使用while循环,循环里面的操作是将该单词插入到vector尾部,最后关闭文件完成读取。完成读取之后修改一下target生成的语句便可以了,程序如下:
// pe3.cpp -- test hangman words form file
#include <iostream>
#include <fstream>
#include <cctype>
#include <string>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <ctime>

int main()
{
    using std::cout;
    using std::string;
    using std::cin;
    using std::vector;
    using std::ifstream;
    vector<string> wordlist;
    string word;            // record word from file
// input words from files
    ifstream inFlie;
    inFlie.open("wordlist.txt");
    if (!inFlie.is_open())
    {
        cout << "Can't open the file. Bye.\n";
        exit(EXIT_FAILURE);
    }
    while (inFlie >> word)
        wordlist.push_back(word);
    inFlie.close();
// hangman game
    char play;
    cout << "Will you play the game? <y/n>";
    cin >> play;
    play = tolower(play);
    while (play == 'y')
    {
        srand(time(0));
        string target = wordlist[rand() % wordlist.size()];
        int length = target.size();
        string attempt(length, '-');
        string badchars;
        int guesses = target.size();
        cout << "Guess my secret word. It has " << length 
             << " letters, and your guess\n"
             << "one letter at a time. You get "<< guesses
             << " wrong guesses.\n";
        while (guesses > 0 && attempt != target)
        {
            char letter;
            cout << "Guess a letter: ";
            cin >> letter;
            if (attempt.find(letter) != string::npos || 
                badchars.find(letter) != string::npos)
            {
                cout << "You have guessed the letter. Try again.\n";
                continue;
            }
            int pos = target.find(letter);
            if (pos == string::npos)
            {
                cout << "Oh, bad guess!\n";
                badchars += letter;
                guesses--;
            }
            else
            {
                cout << "Good guess!\n";
                while (pos != string::npos)
                {
                    attempt[pos] = letter;
                    pos = target.find(letter,pos + 1);
                }
            }
            cout << "Your word: " << attempt << std::endl;
            if (attempt != target)
            {
                if (badchars.size() > 0)
                    cout << "Bad choices: " << badchars << std::endl;
                cout  << guesses << " bad guesses left\n";
            }
        }

        if (guesses > 0)
            cout << "Congratlations! You guess the word: " << target << std::endl;
        else
            cout << "Sorry, the word is " << target << std::endl;
        cout << "Will you play another? <y/n>";
        cin >> play;
    }
    cout << "Done\n";
    return 0;
}

wordlist.txt文件如下:
apiary beetle cereal
danger ensign florid garage health insult
jackal keeper losner manage nonce onset
plaid quilt remote stolid train useful
valid whence xenon yearn zippy
注意wordlist.txt应该与C++文件在同一个目录下,如果不在需要输入文件的绝对路径。运行结果如下:

image
image

4. 编写一个具有老式风格接口的函数,其原型如下:

int reduce(long ar[], int n);
实参应是数组名和数组中的元素个数。该函数对数组进行排序,删除重复的值,返回缩减后数组中的元素数目。请使用STL函数编写该函数(如果决定使用通用的unqiue()函数,请注意它将返回结果区间的结尾)。请使用一个小程序测试该函数。

本题有两种实现方法,一种是使用set容器,只需要完成将ar的元素插入到set容器中,然后再将set容器的内容复制进ar数组里就完成了,数组的新长度可以用set容器的.size()方法,程序如下:

// pe4.cpp -- definition of int reduce(long ar[], int n) and use STL
#include <iostream>
#include <set>
#include <cstdlib>
#include <ctime>

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

int main()
{
    using std::cout;
    using std::endl;
    srand(time(0));
    const int LIM = 20;
    long testar[LIM];
    for (int i = 0; i < LIM; i++)
        testar[i] = rand() % 10;
    cout << "Before using reduce() function, the test array:\n";
    for (int i = 0; i < LIM; i++)
    {
        cout << testar[i] << " ";
        if (i % 10 == 9)
            cout << "\n";
    }
    if (LIM % 10 != 0)
        cout << endl;
    int len = reduce(testar, LIM);
    cout << "After using reduce() function, the test array:\n";
    for (int i = 0; i < len; i++)
    {
        cout << testar[i] << " ";
        if (i % 10 == 9)
            cout << "\n";
    }
    if (len % 10 != 0)
        cout << endl;
    cout << "Done\n";
    return 0;

}

int reduce(long ar[], int n)
{
    std::set<long> temp;
    // std::vector<long> temp(n);
    for (int i = 0; i < n; i++)
        temp.insert(ar[i]);
    int len = temp.size();
    int i;
    copy(temp.begin(), temp.end(), ar);
    return len;
}

另一种方法使用sort()对数组进行排序,排序完成之后使用unique完成删除相同的元素,之后用返回的超尾迭代器减去ar首端的地址即为ar的新长度,程序如下:

// version use sort and unique
int reduce(long ar[], int n)
{
    std::sort(ar, ar + n);
    auto air = std::unique(ar, ar + n);
    return (air - ar) ;
}

运行结果如下:
image

5. 问题与编程练习四相同,但要编写一个模板函数:

template<class T>
int reduce(long ar[], int n);
在使用一个long实例和string实例的小程序中测试该函数。

本题很简单,要修改的地方不多,对于sort()、unique()版本,只需要修改函数参数的声明就可以了,而se版本在此基础上修改了set声明就可以了,程序如下:

// pe5.cpp -- template definition of int reduce(long ar[], int n) and use STL

// pe4.cpp -- definition of int reduce(long ar[], int n) and use STL
#include <iostream>
#include <set>
#include <algorithm>
#include <cstdlib>
#include <ctime>

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

int main()
{
    using std::cout;
    using std::endl;
    srand(time(0));
// test long numbers
    const int LIM = 20;
    long testar[LIM];
    for (int i = 0; i < LIM; i++)
        testar[i] = rand() % 10;
    cout << "Before using reduce() function, the test array:\n";
    for (int i = 0; i < LIM; i++)
    {
        cout << testar[i] << " ";
        if (i % 10 == 9)
            cout << "\n";
    }
    if (LIM % 10 != 0)
        cout << endl;
    int len = reduce<long>(testar, LIM);
    cout << "After using reduce() function, the test array:\n";
    for (int i = 0; i < len; i++)
    {
        cout << testar[i] << " ";
        if (i % 10 == 9)
            cout << "\n";
    }
    if (len % 10 != 0)
        cout << endl;
// test string
    std::string test_sar[LIM] = {"Union", "for", "cat", "dog", "for", "why", "how", "what", "want", "cat",
        "white", "can", "air", "dog", "for", "why", "how", "what", "want", "cat"};
    cout << "Before using reduce() function, the test array:\n";
    for (int i = 0; i < LIM; i++)
    {
        cout << test_sar[i] << " ";
        if (i % 10 == 9)
            cout << "\n";
    }
    if (LIM % 10 != 0)
        cout << endl;
    len = reduce<std::string>(test_sar, LIM);
    cout << "After using reduce() function, the test array:\n";
    for (int i = 0; i < len; i++)
    {
        cout << test_sar[i] << " ";
        if (i % 10 == 9)
            cout << "\n";
    }
    if (len % 10 != 0)
        cout << endl;
    cout << "Done\n";
    return 0;

}
// version use set
template <class T>
int reduce(T ar[], int n)
{
    std::set<T> temp;
    // std::vector<long> temp(n);
    for (int i = 0; i < n; i++)
        temp.insert(ar[i]);
    int len = temp.size();
    int i;
    copy(temp.begin(), temp.end(), ar);
    return len;
}

// // version use sort and unique
// template <class T>
// int reduce(T ar[], int n)
// {
//     std::sort(ar, ar + n);
//     auto air = std::unique(ar, ar + n);
//     return (air - ar) ;
// }

运行结果如下:
image

6.使用STL queue模板类而不是第12章的Queue类,重新编写程序清单12.12所示的示例。

本题不算难,首先添加queue头文件,然后添加顾客类的定义,方法实现,修改生成的队列line,不限制长度,在判断队列是否满处修改代码为if(line.size() == qs),入队改用方法push,出队用方法pop,出对之前,用line.front()方法读取队首的类。程序如下:

// pe6.cpp use queue to realize bank
#include <iostream>
#include <cstdlib>      // for rand() and srand()
#include <ctime>        // for time()
#include <queue>
const int MIN_PER_HR = 60;

// Custormer items
class Customer
{
private:
    long arrive;                // arrive 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; }
};
typedef Customer Item;
// time set to a random value in the range 1 - 3
void Customer::set(long when)
{
    processtime = std::rand() % 3 + 1;
    arrive = when;
}

bool newcustomer(double x); // is there a new customer

int main()
{
    using std::cin;
    using std::cout;
    using std::endl;
    using std::ios_base;
    using std::queue;
// setting things up
    std::srand(std::time(0));   // random intializing of rand

    cout << "Case Study: Bank of Heather Automatic Teller\n";
    cout << "Enter the maximum size of queue: ";
    int qs;
    cin >> qs;
    queue<Item> line;            // line queue holds up to qs people

    cout << "Enter the number of simulation hours: ";
    int hours;
    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;
    cin >> perhour;
    double min_per_cust;
    min_per_cust = MIN_PER_HR / perhour;

    Item temp;                 // new customer data
    long turnaways = 0;        // turn away by full queue
    long customers = 0;        // joined the queue
    long served = 0;           // served during the simulation
    long sum_line = 0;         // cmulative line length
    long wait_time = 0;        // time untile 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.push(temp);
                // line.enqueue(temp);     // add newcomer to line
            }
        }
        if(wait_time <= 0 && !line.empty())
        {
            temp = line.front();
            line.pop();         // attend next customer
            wait_time = temp.Ptime();   // for wait_time minuetes
            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 << " avergae wait time: "
             << (double) line_wait / served << "minutes\n";
    }
    else
        cout << "No customerr!\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);
}

运行结果如下:
image

7. 彩票卡是一个常见的游戏。卡片上是带编号的圆点,其中一些圆点被随机选中。编写一个lotto()函数,它接受两个参数。第一个参数是彩票卡上圆点的个数,第二个参数是随机选择的圆点的个数。该函数返回一个vector<int>对象,其中包含(按排列后的顺序)随机选择的号码。例如,可以这样使用该函数:

vector<int>winners;
winners = Lotto(51,6);
这样将一个矢量赋给winner,该矢量包含1~51随机选定的6个数字。注意,仅仅使用rand()无法完成这项任务,因为它会生成重复的值。提示:让函数创建一个包含所有可能值的矢量,使用random_shuffle(),然后通过打乱后的矢量的第一个值来获取值。
本题采用了题里提示的思路,使用随机打乱后的第一个值来获取值,测试时发现值也有可能重复,因此加了一个while循环,判断第一个值在不在结果里,若在,重新随机生成一个顺序,直到第一个值不在结果容器里,取第一个值;还有一种方法是生成一个随机顺序,在这个随机顺序里随机取6个值,如果重复,不添加,直到不重复位置。这里采用第一种方法,程序如下:

// pe7.cpp -- simulation of lottery card
#include <iostream>
#include <vector>
#include <algorithm>

using std::vector;

vector<int> Lotto(int numbers, int num_choices);
void show(int x) { std::cout << x << ' ';}
int main()
{
    using std::cout;
    using std::cin;
    using std::endl;
    int numebrs, num_choices;
    cout << "Enter the numbers of point and the numebr of chooses <q to quit>: ";
    while (cin >> numebrs >> num_choices)
    {
        auto winner = Lotto(numebrs,num_choices);
        cout << "The winner's numbers is ";
        std::for_each(winner.begin(), winner.end(), show);
        cout << endl;
        cout << "Enter next two numbers <q to quit>: ";
    }
    return 0;
}

vector<int> Lotto(int numbers, int num_choices)
{
    vector<int> temp;
    vector<int> result;
    for (int i = 1; i <= numbers; i++)
        temp.push_back(i);
    for (int i = 0; i < num_choices; i++)
    {
        std::random_shuffle(temp.begin(), temp.end());
    // make sure the value not repeat
        while (find(result.begin(), result.end(),temp[0]) != result.end())
            std::random_shuffle(temp.begin(), temp.end());
        result.push_back(temp[0]);
    }
    return result;
}

运行结果如下:
image

8. Mat和Pat希望邀请他们的朋友来参加派对。它们要编写一个程序完成下面的任务。

  • 让Mat输入他朋友的姓名列表。姓名存储在一个容器中,然后按排列顺序显式出来。
  • 让Pat输入她的朋友的姓名列表。姓名存储在另一个容器中,然后按排列后的顺序显式出来。
  • 创建第三个容器,将两个列表合并,删除重复的部分,并显示这个容器的内容。

本题不算难,使用set容器来解决该问题,编写程序时,在set_union()函数的使用上出现了问题,第五个迭代器应该是一个输出迭代器,而pm_union.begin()是一个输入迭代器,因此编译时引发了错误,添加insert_iterator迭代器之后,程序正确,程序如下:

// pe8.cpp -- use set and set_union
#include <iostream>
#include <set>
#include <string>
#include <algorithm>
#include <iterator>

void show(const std::string & s) { std::cout << s << "; "; }
int main()
{
    using std::cout;
    using std::cin;
    using std::endl;
    using std::set;
    using std::string;
    using std::for_each;
// mat
    set<string> mat;
    string name;
    cout << "Dear mat, please enter your friend's name <quit to quit>: ";
    while (getline(cin, name) && name != "quit")
    {
        mat.insert(name);
        cout << "enter next name <quit to quit>: ";
    }
    cout << "Mat's friend list:\n";
    for_each(mat.begin(), mat.end(), show);
    cout << endl;
// pat
    set<string> pat;
    cout << "Dear pat, please enter your friend's name <quit to quit>: ";
    while (getline(cin, name) && name != "quit")
    {
        pat.insert(name);
        cout << "enter next name <quit to quit>: ";
    }
    cout << "Pat's friend list:\n";
    for_each(pat.begin(), pat.end(), show);
    cout << endl;
// set_union
    set<string> pm_union;
    std::set_union(mat.begin(), mat.end(), pat.begin(), pat.end(), 
        std::insert_iterator< set<string> >(pm_union, pm_union.begin()));
    cout << "Pat and Mat friend list:\n";
    for_each(pm_union.begin(), pm_union.end(), show);
    cout << endl;
    cout << "Done\n";
    return 0;
}

运行结果如下:
image

9. 相对于数组,在链表中添加和删除元素更容易,但排序速度更慢。这就引出了一种可能性:相对于使用链表算法进行排序,将链表复制到数组中,对数组进行排序,再将排序后的结果复制到链表中的速度可能更快;但这也可能占用更多的内存。请使用如下方法检验上述假设。

a. 创建大型vector<int>对象vi0,并使用rand()给它提供初始值。
b. 创建vector<int>对象vi和list<int>对象li,它们的长度都和初始值vi0相同。
c. 计算使用STL算法sort()对vi进行排序所需的时间,再计算使用list的方法与sort()进行排序所需的时间。
d. 将li重置为排序的v10的内容,并计算执行如下操作所需的时间:将li的内容复制到vi中,对vi进行排序,并将结果复制到li中。
要计算这些操作所需的时间,可使用ctime库中的clock()。正如程序清单5.14演示的,可使用下面的语句来获取开始时间:
clock_t start = clock();
再在操作结束后使用下面的语句获取经过了多长时间:
clock_t end = clock();
cout << (double)(end - start)/CLOCKS_PER_SEC;
这种测试并非绝对可靠,因为结果取决于很多因素,如可用内存量、是都支持多处理以及数组(列表)的长度(对着要排序的元素数增加,数组相对于列表的效率将更明显)。另外,如果编译器提供了默认生成的方式和发布生成方式,请使用发布生成方式。鉴于当今计算机的速度非常快,要获得有意义的结果,可能需要使用尽可能大的数组。例如,可尝试包含100000、1000000和10000000个元素。

本题不算难,但是有几个需要注意的地方。首先使用copy时第三个迭代器应该时插入类型的迭代器,即insert_iterator类型的类型的时间。完成b、c之后,在复制vi0到li之前应该先将li和vi擦除。程序如下:

// pe9.cpp -- compare the speed list.sort and copy list to vector, sort, and copy to list
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <iterator>

const int LIM = 10000000;         // numbers of elements

int main()
{
    using std::cout;
    using std::sort;
    using std::list;
    using std::vector;
    using std::copy;
    using std::endl;
    using std::insert_iterator;

    cout << "Number of elements: " << LIM << endl;
// generate elements of vector<int> vi0
    srand(time(0));             // generate random seeds
    vector<int> vi0;
    for (int i = 0; i < LIM; i++)
        vi0.push_back(rand() % LIM);
// copy vi0 to ci and li
    vector<int> vi;
    list<int> li;
    copy(vi0.begin(), vi0.end(), insert_iterator<vector<int>>(vi, vi.begin()));
    copy(vi0.begin(), vi0.end(), insert_iterator<list<int>>(li, li.begin()));
/**** calculate the time of sort(vi) and li.sort() ****/
// calculate the time of sort(vi.begin(), vi.end());
    clock_t start = clock();
    sort(vi.begin(), vi.end());
    clock_t end = clock();
    cout << "The time of sort(vi.begin(), vi.end()):\n";
    cout << (double)(end - start) / CLOCKS_PER_SEC;
    cout << endl;
// calculate the time of list.sort();
    start = clock();
    li.sort();
    end = clock();
    cout << "The time of li.sort():\n";
    cout << (double)(end - start) / CLOCKS_PER_SEC;
    cout << endl;

/**** calculate the time of copy() + sort(vi.begin(),vi.end()) + copy() ****/
// clear list and vi
    li.erase(li.begin(), li.end());
    vi.erase(vi.begin(), vi.end());
// calculate time
    copy(vi0.begin(), vi0.end(), insert_iterator<list<int>>(li, li.begin()));
    start = clock();
    copy(li.begin(), li.end(), insert_iterator<vector<int>>(vi, vi.begin()));
    sort(vi.begin(), vi.end());
    copy(vi.begin(), vi.end(), insert_iterator<list<int>>(li, li.begin()));
    end = clock();
    cout << "The time of combination:\n";
    cout << (double)(end - start) / CLOCKS_PER_SEC;
    cout << endl;

    return 0;

}

分别设置元素数为100000、1000000、10000000,运行结果如下:
image
image
image
最后一个没有使用vscode自带的调试工具,猜测该工具可能限制了内存,最后一个结果计算不出来,使用了命令行运行,可以计算出结果。

10. 请按如下方式修改程序清单16.9(vect3.cpp)。

a. 在结构Review中添加成员price。
b. 不要使用vector<Review>来存储输入,而使用vector<shared_ptr<Review>>。别忘了必须使用new的返回指针来初始化shared_ptr。
c. 在输入阶段结束后,使用一个循环让用户选择如下方式之一显示书籍:按原始顺序显示、按字母表顺序显示、按评级升序显示、按评级降序显示、按价格升序显示,按价格降序显示、退出。
下面是一种可能的解决方案:获取输入后,在创建一个shared_ptr矢量,并用原始数组初始化它。定义一个对指向结构的指针进行比较的operator<()函数,并使用它对第二个矢量进行排序,让其中的shared_ptr按其指向对象中的书名排序。重复上述过程,创建按rating和price排序的shared_ptr矢量。请注意,通过使用rbegin()和rend(),可避免创建相反的顺序排列的shared_ptr矢量。

本题比较复杂,考查了shared_ptr智能指针的使用,排序函数的程序编写,由于名称都比较长,因此可以采用typedef简化编码。程序如下:

// pe10.cpp -- using STL functions and shared_ptr
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <memory>
#include <iterator>


struct Review{
    std::string title;
    int rating;
    double price;
};
// using name
    using std::cin;
    using std::cout;
    using std::vector;
    using std::for_each;
    using std::shared_ptr;
    using std::sort;
    using std::copy;
    using std::insert_iterator;
// simplify the name
    typedef shared_ptr<Review> SpR;
    typedef vector<SpR>  VSpR;
    typedef insert_iterator<VSpR> insert_ir_VSpR;

bool FillReview(Review & rr);                           // input a book
SpR make_Review(Review & rr);       // return a shared_ptr of a book
void ShowMenu();                                        // display the menu
void ShowReview(const SpR spr);    // show a book
// compare the book of title 
bool operator<(const SpR spr1, const SpR spr2);
// compare the book of rating 
bool compare_rating(const SpR spr1, const SpR spr2);
// compare the book of price
bool compare_price(const SpR spr1, const SpR spr2);
void ShowBooks(const VSpR & vspr);
int main()
{
// input the book to books_spr
    VSpR books_ptr;
    vector<Review> books;
    Review temp;
    while (FillReview(temp))
        books_ptr.push_back(make_Review(temp));
    
    VSpR temp_books_ptr;    // prototype a temp_books_ptr to restore result
// output menu
    ShowMenu();
// display books_list
    char choice;
    while (cin >> choice && choice != 'q')
    {
    // if input other char
        if(!strchr("abcdef",choice))
        {
            cout << "Please enter a a, b, c, d, e, f, q <q to quit>: ";
            continue;
        }
        switch (choice)
        {
            case 'a': cout << "Orignal order:\nRating\tBook\tprice\n";
                      for_each(books_ptr.begin(), books_ptr.end(), ShowReview);
                      break;
            case 'b': temp_books_ptr.erase(temp_books_ptr.begin(), 
                            temp_books_ptr.end());
                      copy(books_ptr.begin(), books_ptr.end(), 
                            insert_ir_VSpR(temp_books_ptr, temp_books_ptr.begin()));
                      cout << "Letter order:\nRating\tBook\tprice\n";
                      sort(temp_books_ptr.begin(), temp_books_ptr.end());
                      for_each(temp_books_ptr.begin(), temp_books_ptr.end(), ShowReview);
                      break;
            case 'c': temp_books_ptr.erase(temp_books_ptr.begin(), 
                            temp_books_ptr.end());
                      copy(books_ptr.begin(), books_ptr.end(), 
                            insert_ir_VSpR(temp_books_ptr, temp_books_ptr.begin()));
                      cout << "Rating ascending order:\nRating\tBook\tprice\n";
                      sort(temp_books_ptr.begin(), temp_books_ptr.end(),compare_rating);
                      for_each(temp_books_ptr.begin(), temp_books_ptr.end(), ShowReview);
                      break;
            case 'd': temp_books_ptr.erase(temp_books_ptr.begin(), 
                            temp_books_ptr.end());
                      copy(books_ptr.begin(), books_ptr.end(), 
                            insert_ir_VSpR(temp_books_ptr, temp_books_ptr.begin()));
                      cout << "Rating decending order:\nRating\tBook\tprice\n";
                      sort(temp_books_ptr.begin(), temp_books_ptr.end(),compare_rating);
                      for_each(temp_books_ptr.rbegin(), temp_books_ptr.rend(), ShowReview);
                      break;
            case 'e': temp_books_ptr.erase(temp_books_ptr.begin(), 
                            temp_books_ptr.end());
                      copy(books_ptr.begin(), books_ptr.end(), 
                            insert_ir_VSpR(temp_books_ptr, temp_books_ptr.begin()));
                      cout << "Price ascending order:\nRating\tBook\tprice\n";
                      sort(temp_books_ptr.begin(), temp_books_ptr.end(),compare_price);
                      for_each(temp_books_ptr.begin(), temp_books_ptr.end(), ShowReview);
                      break;
            case 'f': temp_books_ptr.erase(temp_books_ptr.begin(), 
                            temp_books_ptr.end());
                      copy(books_ptr.begin(), books_ptr.end(), 
                            insert_ir_VSpR(temp_books_ptr, temp_books_ptr.begin()));
                      cout << "Price decending order:\nRating\tBook\tprice\n";
                      sort(temp_books_ptr.begin(), temp_books_ptr.end(),compare_price);
                      for_each(temp_books_ptr.rbegin(), temp_books_ptr.rend(), ShowReview);
                      break;
        }
        ShowMenu();
    }
    cout << "Done";
    return 0;
}

bool FillReview(Review & rr)
{
    std::cout << "Enter the book title (quit to quit): ";
    std::getline(std::cin, rr.title);
    if (rr.title == "quit")
        return false;
    std::cout << "Enter book rating: ";
    std::cin >> rr.rating;
    std::cout << "Enter book price: ";
    std::cin >> rr.price;
    while (std::cin.get() != '\n')
        continue;
    return true;
}

SpR make_Review(Review & rr)
{
    return SpR(new Review(rr));
}
void ShowMenu()
{
    using std::cout;
    using std::endl;
    cout << "Choice the display order <q to quit>:\n";
    cout << "a: orignal order       b: letter order\n";
    cout << "c: rating ascending    d: rating decending\n";
    cout << "e: price ascendding    f: price decending\n";
    cout << "q: quit\n";
}

void ShowReview(const SpR spr)
{
    std::cout << spr->rating << "\t" << spr->title << "\t$" << spr->price << std::endl;
}

bool operator<(const SpR spr1, const SpR spr2)
{
    if (spr1->title < spr2->title)
        return true;
    else if (spr1->title == spr2->title && spr1->rating < spr2->rating)
        return true;
    else if (spr1->title == spr2->title && spr1->rating == spr2->rating
        && spr1->price < spr2->price)
        return true;
    else
        return false;
}

bool compare_rating(const SpR spr1, const SpR spr2)
{
    if (spr1->rating < spr2->rating)
        return true;
    else if (spr1->rating == spr2->rating && spr1->title < spr2->title)
        return true;
    else if (spr1->title == spr2->title && spr1->rating == spr2->rating
        && spr1->price < spr2->price)
        return true;
    else
        return false;
}

bool compare_price(const SpR spr1, const SpR spr2)
{
    if (spr1->price < spr2->price)
        return true;
    else if (spr1->price == spr2->price && spr1->title < spr2->title)
        return true;
    else if (spr1->title == spr2->title && spr1->price == spr2->price
        && spr1->rating < spr2->rating)
        return true;
    else
        return false;
}

运行结果如下:
image
image

posted @ 2022-01-12 13:38  Fight!GO  阅读(286)  评论(0编辑  收藏  举报