《C++ Primer》第9章 9.2节习题答案

《C++ Primer》第9章 顺序容器

9.2节 容器库概览习题答案

练习9.2:定义一个list对象,其元素类型是int的deque。

【出题思路】本题练习容器的定义。

【解答】

list< deque<int> > a;

练习9.3:构成迭代器范围的迭代器有何限制?

【出题思路】

准确理解怎样的迭代器可以构成迭代器范围。

【解答】

两个迭代器begin和end必须指向同一个容器中的元素,或者是容器最后一个元素之后的位置;而且,对begin反复进行递增操作,可保证到达end,即end不在begin之前。

#include <iostream>
#include  <vector>

using namespace std;

bool search_vector(vector<int>::iterator beg, vector<int>::iterator end, int val)
{
    for(; beg != end; ++beg)
        if(*beg == val)
            return true;
    return false;
}

int main()
{
    vector<int> vec = {1,2,3,4,5,6,7,8};
    cout << "search_vector(vec.begin(), vec.end(), 3) = " << search_vector(vec.begin(), vec.end(), 3) << endl;
    cout << "search_vector(vec.begin(), vec.end(), 8) = " << search_vector(vec.begin(), vec.end(), 8) << endl;
    cout << "search_vector(vec.begin(), vec.end(), 9) = " << search_vector(vec.begin(), vec.end(), 10) << endl;
    cout << "Hello World!" << endl;
    return 0;
}

运行结果:

练习9.5:重写上一题的函数,返回一个迭代器指向找到的元素。注意,程序必须处理未找到给定值的情况。

【出题思路】

练习如何用迭代器表示搜索成功和搜索失败。

【解答】

#include <iostream>
#include <vector>

using namespace std;

vector<int>::iterator search_vector(vector<int>::iterator beg, vector<int>::iterator end, int val)
{
    for(; beg != end; ++beg)
        if(*beg == val)
            return beg;
    return end;
}

int main()
{
    vector<int> vec = {1,2,3,4,5,6,7,8};
    cout << "search_vector(vec.begin(), vec.end(), 3) = "
         << search_vector(vec.begin(), vec.end(), 3) - vec.begin() << endl;
    cout << "search_vector(vec.begin(), vec.end(), 8) = "
         << search_vector(vec.begin(), vec.end(), 8) - vec.begin() << endl;
    cout << "search_vector(vec.begin(), vec.end(), 9) = "
         << search_vector(vec.begin(), vec.end(), 9) - vec.begin() << endl;
    cout << "Hello World!" << endl;
    return 0;
}

运行结果:

 

练习9.6:下面程序有何错误?你应该如何修改它?

list<int> lst1;
list<int>::iterator iter1 = lst1.begin(), iter2 = lst1.end();
while(iter1 < iter2)  /* ... */

 【出题思路】

理解不同类型容器的迭代器之间的差别。更深层次的,理解数据结构的实现如何导致迭代器的差别。

【解答】

与vector和deque不同,list的迭代器不支持<运算,只支持递增、递减、==以及!=运算。原因在于这几种数据结构实现上的不同。vector和deque将元素在内存中连续保存,而list则是将元素以链表方式存储,因此前者可以方便地实现迭代器的大小比较(类似指针的大小比较)来体现元素的前后关系。而在list中,两个指针的大小关系与它们指向的元素的前后关系并不一定是吻合的,实现<运算将会非常困难和低效。

练习9.7:为了索引int的vector中的元素,应该使用什么类型?

【出题思路】

标准库容器定义了若干类型成员,对应容器使用中可能涉及的类型,如迭代器、元素引用等。本题和下一题帮助读者理解这些类型。

【解答】

使用迭代器类型vector<int>::iterator来索引int的vector中的元素。

练习9.8:为了读取string的list中的元素,应该使用什么类型?如果写入list,又该使用什么类型?【出题思路】

理解容器的类型成员。

【解答】

为了读取string的list中的元素,应使用list<string>::value_type,因为value_type表示元素类型。为了写入数据,需要(非常量)引用类型,因此应使用list <string>::reference。

练习9.9:begin和cbegin两个函数有什么不同?

【出题思路】

begin和end都有多个版本,分别返回普通、反向和const迭代器。本题帮助读者理解不同版本间的差异。

【解答】

cbegin是C++新标准引入的,用来与auto结合使用。它返回指向容器第一个元素的const迭代器,可以用来只读地访问容器元素,但不能对容器元素进行修改。因此,当不需要写访问时,应该使用cbegin。begin则是被重载过的,有两个版本:其中一个是const成员函数,也返回const迭代器;另一个则返回普通迭代器,可以对容器元素进行修改。

练习9.10:下面4个对象分别是什么类型?

vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();

【出题思路】

继续熟悉begin不同版本的差异和不同类型迭代器的差异,特别是begin与auto配合使用时的细微差异。

【解答】

v1是int的vector类型,我们可以修改v1的内容,包括添加、删除元素及修改元素值等操作。v2是int的常量vector类型,其内容不能修改,添加、删除元素及修改元素值等均不允许。

begin与auto结合使用时,会根据调用对象的类型来决定迭代器的类型,因此it1是普通迭代器,可对容器元素进行读写访问,而it2是const迭代器,不能对容器元素进行写访问。而cbegin则不管调用对象是什么类型,始终返回const迭代器,因此it3和it4都是const迭代器。

练习9.11:对6种创建和初始化vector对象的方法,每一种都给出一个实例。解释每个vector包含什么值。

【出题思路】

C++11提供了丰富的容器初始化方式,理解各种初始化方式之间的不同,有助于我们在求解实际问题时选择恰当的方式。

【解答】

(1)vector<int> ilist1; // 默认初始化,vector为空——size返回0,表明容器中尚未有元素;capacity返回0,意味着尚未分配存储空间。这种初始化方式适合于元素个数和值未知,需要在程序运行中动态添加的情况。

(2)vector<int> ilist2(ilist); // ilist2初始化为ilist的拷贝,ilist必须与ilist2类型相同,即也是int的vector类型,ilist2将具有与ilist相同的容量和元素。

vector<int> ilist2_1 = ilist;//等价方式

(3)vector<int> ilist = {1, 2, 3.0, 4, 5, 6, 7}; // ilist初始化为列表中元素的拷贝,列表中的元素类型必须与ilist的元素类型相容,在本例中必须是与整型相容的数值类型。对于整型,会直接拷贝其值,对于其他类型则需进行类型转换(如3.0转换为3)。这种初始化方式适合元素数量和值预先可知的情况。

vector<int> iList3_1{1, 2, 3.0, 4, 5, 6, 7};  //等价方式

(4)vector<int> ilist3(ilist.begin()+2, ilist.end()-1); // ilist3初始化为两个迭代器指定范围中的元素的拷贝,范围中的元素类型必须与ilist3的元素类型相容,在本例中ilist3被初始化为{3, 4, 5, 6}。注意,由于只要求范围中元素类型与待初始化的容器的元素类型相容,因此,迭代器来自于不同类型的容器是可能的,例如,用一个double的list的范围来初始化ilist3是可行的。另外,由于构造函数只是读取范围中的元素并进行拷贝,因此使用普通迭代器还是const迭代器来指出范围并无区别。这种初始化方法特别适合于获取一个序列的子序列。

(5)vector<int> ilist4(7); // 默认值初始化,ilist4中将包含7个元素,每个元素进行缺省的值初始化,对于int,也就是被赋值为0,因此ilist4被初始化为包含7个0。当程序运行初期元素大致数量可预知,而元素的值需动态获取时,可采用这种初始化方式。

(6)vector<int> ilist5(7, 3); // 指定值初始化,ilist5被初始化为包含7个值为3的int。

练习9.12:对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的构造函数,解释它们的不同。

【出题思路】

继续熟悉不同的初始化方式。

【解答】

接受一个已有容器的构造函数会拷贝此容器中的所有元素,这样,初始化完成后,我们得到此容器的一个一模一样的拷贝。当我们确实需要一个容器的完整拷贝时,这种初始化方式非常方便。但当我们不需要已有容器中的全部元素,而只是想拷贝其中一部分元素时,可使用接受两个迭代器的构造函数。传递给它要拷贝的范围的起始和尾后位置的迭代器,即可令新容器对象包含所需范围中元素的拷贝。

练习9.13:如何从一个list<int>初始化一个vector<double>?从一个vector<int>又该如何创建?编写代码验证你的答案。

【出题思路】

更深入地理解容器拷贝初始化和范围初始化两种方式的差异。

【解答】

由于list<int>与vector<double>是不同的容器类型,因此无法采用容器拷贝初始化方式。但前者的元素类型是int,与后者的元素类型double是相容的,因此可以采用范围初始化方式来构造一个vector<double>,令它的元素值与list<int>完全相同。对vector<int>也是这样的思路。

#include <iostream>
#include <vector>
#include <list>

using namespace std;

int main()
{
    list<int> ilist = {1, 2, 3, 4, 5, 6, 7};
    vector<int> ivec = {7, 6, 5, 4, 3, 2, 1};
    //容器类型不同,不能使用拷贝初始化
    //vector<double> ivec(ilist);
    //元素类型相容,因此可用采用范围初化
    vector<double> dvec(ilist.begin(), ilist.end());
    //容器类型不同,不能使用拷贝初始化
    //vector<double> dvect1(ivec);
    //元素类型相容,因此可采用范围初始化
    vector<double> dvec1(ivec.begin(), ivec.end());

    cout << "dvec.capacity() = " << dvec.capacity() << " dvec.size() = " << dvec.size() << " dvec[0] = " << dvec[0]
         << " dvec[dvec.size() - 1] = " << dvec[dvec.size() - 1] << endl;
    cout << "dvec1.capacity()= " << dvec1.capacity() << " dvec1.size()= " << dvec1.size() << " dvec1[0]= " << dvec1[0]
         << " dvec1[dvec1.size() - 1]= " << dvec1[dvec1.size() - 1] << endl;

    return 0;
}

运行结果:

 练习9.14:编写程序,将一个list中的char *指针(指向C风格字符串)元素赋值给一个vector中的string。

【出题思路】

容器有多种赋值操作,本题帮助读者理解不同赋值方式的差异。

【解答】

由于list<char *>与vector<string>是不同类型的容器,因此无法采用赋值运算符=来进行元素赋值。但char *可以转换为string,因此可以采用范围赋值方式来实现本题要求。

#include <iostream>
#include <vector>
#include <list>
#include <string>

using namespace std;

int main()
{
    list<const char *> slist = {"hello", "world", "!"};
    vector<string> svec;
    //容器类型不同,不能直接赋值
    //svec = slist;
    //元素类型相容,可以采用范围赋值
    svec.assign(slist.begin(), slist.end());

    cout << "svec.capacity() = " << svec.capacity() << " svec.size()=" << svec.size()<<endl;
    cout << "svec[0]=" << svec[0] << " svec[svec.size() - 1]=" << svec[svec.size() - 1] << endl;

    return 0;
}

运行结果:

 练习9.15:编写程序,判定两个vector<int>是否相等。

【出题思路】

练习容器的关系运算符的使用。

【解答】

标准库容器支持关系运算符,比较两个vector是否相等使用==运算符即可。当两个vector包含相同个数的元素,且对位元素都相等时,判定两个vector相等,否则不等。两个vector的capacity不会影响相等性判定,因此,当下面程序中ivec1在添加、删除元素导致扩容后,仍然与ivec相等。

#include <iostream>
#include <vector>
#include <list>

using namespace std;

int main()
{
    vector<int> ivec = {1, 2, 3, 4, 5, 6, 7};
    vector<int> ivec1 = {1, 2, 3, 4, 5, 6, 7};
    vector<int> ivec2 = {1, 2, 3, 4, 5};
    vector<int> ivec3 = {1, 2, 3, 4, 5, 6, 8};
    vector<int> ivec4 = {1, 2, 3, 4, 5, 7, 6};

    cout << "(ivec == ivec1) === " << (ivec == ivec1) << endl;
    cout << "(ivec == ivec2) === " << (ivec == ivec2) << endl;
    cout << "(ivec == ivec3) === " << (ivec == ivec3) << endl;
    cout << "(ivec == ivec4) === " << (ivec == ivec4) << endl;

    return 0;
}

运行结果:

 练习9.16:重写上一题的程序,比较一个list<int>中的元素和一个vector<int>中的元素。

【出题思路】

首先,理解容器关系运算符的限制——必须是相容类型的容器,且元素类型也必须相同才能比较。其次,练习自己编写程序来实现容器内容的比较。

【解答】

两个容器相等的充分条件是包含相同个数的元素,且对位元素的值都相等。因此,首先判断两个容器是否包含相同个数的元素,若不等,则两个容器也不等。否则,遍历两个容器中的元素,两两比较对位元素的值,若有元素不相等,则容器不等。否则,两个容器相等。

#include <iostream>
#include <vector>
#include <list>

using namespace std;

bool l_v_equal(vector<int> &ivec, list<int> &ilist)
{
    //比较list和vector元素个数
    if(ilist.size() != ivec.size())
        return false;

    auto lb = ilist.cbegin();       //list首元素
    auto le = ilist.cend();         //list尾后元位置
    auto vb = ivec.cbegin();        //vector首元素
    for(; lb != le; ++lb, ++vb)
    {
        if(*lb != *vb)              //元素不等,容器不等
            return false;
    }

    return true;                    //容器相等
}

int main()
{
    vector<int> ivec = {1, 2, 3, 4, 5, 6, 7};
    list<int> ilist = {1, 2, 3, 4, 5, 6, 7};
    list<int> ilist1 = {1, 2, 3, 4, 5};
    list<int> ilist2 = {1, 2, 3, 4, 5, 6, 8};
    list<int> ilist3 = {1, 2, 3, 4, 5, 7, 6};

    cout << "l_v_equal(ivec, ilist)  = " << l_v_equal(ivec, ilist) << endl;
    cout << "l_v_equal(ivec, ilist1) = " << l_v_equal(ivec, ilist1) << endl;
    cout << "l_v_equal(ivec, ilist2) = " << l_v_equal(ivec, ilist2) << endl;
    cout << "l_v_equal(ivec, ilist3) = " << l_v_equal(ivec, ilist3) << endl;

    return 0;
}

运行结果:

练习9.17:假定c1和c2是两个容器,下面的比较操作有何限制(如果有的话)?

 if(c1 < c2)

【出题思路】

理解容器关系运算符对容器类型和元素类型的限制。

【解答】

首先,容器类型必须相同,元素类型也必须相同。其次,元素类型必须支持<运算符。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值