当前位置:嗨网首页>书籍在线阅读

16-对矢量可执行的其他操作

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

16.3.3 对矢量可执行的其他操作

程序员通常要对数组执行很多操作,如搜索、排序、随机排序等。矢量模板类包含了执行这些常见的操作的方法吗?没有!STL从更广泛的角度定义了非成员(non-member)函数来执行这些操作,即不是为每个容器类定义find()成员函数,而是定义了一个适用于所有容器类的非成员函数find()。这种设计理念省去了大量重复的工作。例如,假设有8个容器类,需要支持10种操作。如果每个类都有自己的成员函数,则需要定义80(8*10)个成员函数。但采用STL方式时,只需要定义10个非成员函数即可。在定义新的容器类时,只要遵循正确的指导思想,则它也可以使用已有的10个非成员函数来执行查找、排序等操作。

另一方面,即使有执行相同任务的非成员函数,STL有时也会定义一个成员函数。这是因为对有些操作来说,类特定算法的效率比通用算法高,因此,vector的成员函数swap()的效率比非成员函数swap()高,但非成员函数让您能够交换两个类型不同的容器的内容。

下面来看3个具有代表性的STL函数:for_each()、random_shuffle()和sort()。for_each()函数可用于很多容器类,它接受3个参数。前两个是定义容器中区间的迭代器,最后一个是指向函数的指针(更普遍地说,最后一个参数是一个函数对象,函数对象将稍后介绍)。for_each()函数将被指向的函数应用于容器区间中的各个元素。被指向的函数不能修改容器元素的值。可以用for_each()函数来代替for循环。例如,可以将代码:

vector<Review>::iterator pr;
for (pr = books.begin(); pr != books.end(); pr++)
    ShowReview(*pr);

替换为:

for_each(books.begin(), books.end(), ShowReview);

这样可避免显式地使用迭代器变量。

Random_shuffle()函数接受两个指定区间的迭代器参数,并随机排列该区间中的元素。例如,下面的语句随机排列books矢量中所有元素:

random_shuffle(books.begin(), books.end());

与可用于任何容器类的for_each不同,该函数要求容器类允许随机访问,vector类可以做到这一点。

sort()函数也要求容器支持随机访问。该函数有两个版本,第一个版本接受两个定义区间的迭代器参数,并使用为存储在容器中的类型元素定义的<运算符,对区间中的元素进行操作。例如,下面的语句按升序对coolstuff的内容进行排序,排序时使用内置的<运算符对值进行比较:

vector<int> coolstuff;
...
sort(coolstuff.begin(), coolstuff.end());

如果容器元素是用户定义的对象,则要使用sort(),必须定义能够处理该类型对象的operator<()函数。例如,如果为Review提供了成员或非成员函数operator<(),则可以对包含Review对象的矢量进行排序。由于Review是一个结构,因此其成员是公有的,这样的非成员函数将为:

bool operator<(const Review & r1, const Review & r2)
{
    if (r1.title < r2.title)
        return true;
    else if (r1.title == r2.title && r1.rating < r2.rating)
        return true;
    else
        return false;
}

有了这样的函数后,就可以对包含Review对象(如books)的矢量进行排序了:

sort(books.begin(), books.end());

上述版本的operator<()函数按title成员的字母顺序排序。如果title成员相同,则按照rating排序。然而,如果想按降序或是按rating(而不是title)排序,该如何办呢?可以使用另一种格式的sort()。它接受3个参数,前两个参数也是指定区间的迭代器,最后一个参数是指向要使用的函数的指针(函数对象),而不是用于比较的operator<()。返回值可转换为bool,false表示两个参数的顺序不正确。下面是一个例子:

bool WorseThan(const Review & r1, const Review & r2)
{
    if (r1.rating < r2.rating)
        return true;
    else
        return false;
}

有了这个函数后,就可以使用下面的语句将包含Review对象的books矢量按rating升序排列:

sort(books.begin(), books.end(), WorseThan);

注意,与operator<()相比,WorseThan()函数执行的对Review对象进行排序的工作不那么完整。如果两个对象的title成员相同,operator<()函数将按rating进行排序,而WorseThan()将它们视为相同。第一种排序称为全排序(total ordering),第二种排序称为完整弱排序(strict weak ordering)。在全排序中,如果a<b和b<a都不成立,则a和b必定相同。在完整弱排序中,情况就不是这样了。它们可能相同,也可能只是在某方面相同,如WorseThan()示例中的rating成员。所以在完整弱排序中,只能说它们等价,而不是相同。

程序清单16.9演示了这些STL函数的用法。

程序清单16.9 vect3.cpp

// vect3.cpp -- using STL functions
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
struct Review {
    std::string title;
    int rating;
};
bool operator<(const Review & r1, const Review & r2);
bool worseThan(const Review & r1, const Review & r2);
bool FillReview(Review & rr);
void ShowReview(const Review & rr);
int main()
{
    using namespace std;
    vector<Review> books;
    Review temp;
    while (FillReview(temp))
        books.push_back(temp);
    if (books.size() > 0)
    {
        cout << "Thank you. You entered the following "
             << books.size() << " ratings:\n"
              << "Rating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
        sort(books.begin(), books.end());
        cout << "Sorted by title:\nRating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
        sort(books.begin(), books.end(), worseThan);
        cout << "Sorted by rating:\nRating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
        random_shuffle(books.begin(), books.end());
        cout << "After shuffling:\nRating\tBook\n";
        for_each(books.begin(), books.end(), ShowReview);
    }
    else
        cout << "No entries. ";
    cout << "Bye.\n";
    return 0;
}
bool operator<(const Review & r1, const Review & r2)
{
    if (r1.title < r2.title)
        return true;
    else if (r1.title == r2.title && r1.rating < r2.rating)
        return true;
    else
        return false;
}
bool worseThan(const Review & r1, const Review & r2)
{
    if (r1.rating < r2.rating)
        return true;
    else
        return false;
}
bool FillReview(Review & rr)
{
    std::cout << "Enter 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;
    if (!std::cin)
        return false;
    // get rid of rest of input line
    while (std::cin.get() != '\n')
        continue;
    return true;
}
void ShowReview(const Review & rr)
{
    std::cout << rr.rating << "\t" << rr.title << std::endl;
}

程序清单16.9中程序的运行情况如下:

Enter book title (quit to quit): The Cat Who Can Teach You Weight Loss
Enter book rating: 8
Enter book title (quit to quit): The Dogs of Dharma
Enter book rating: 6
Enter book title (quit to quit): The Wimps of Wonk
Enter book rating: 3
Enter book title (quit to quit): Farewell and Delete
Enter book rating: 7
Enter book title (quit to quit): quit
Thank you. You entered the following 4 ratings:
Rating Book
8      The Cat Who Can Teach You Weight Loss
6      The Dogs of Dharma
3      The Wimps of Wonk
7      Farewell and Delete
Sorted by title:
Rating Book
7      Farewell and Delete
8      The Cat Who Can Teach You Weight Loss
6      The Dogs of Dharma
3      The Wimps of Wonk
Sorted by rating:
Rating Book
3      The Wimps of Wonk
6      The Dogs of Dharma
7      Farewell and Delete
8      The Cat Who Can Teach You Weight Loss
After shuffling:
Rating Book
7      Farewell and Delete
3      The Wimps of Wonk
6      The Dogs of Dharma
8      The Cat Who Can Teach You Weight Loss
Bye.