C++ Primer第九章②

C++ Primer

顺序容器

顺序容器操作

前面介绍的那些是所有容器都支持的,我们接下来介绍的只适用于顺序容器(以后还会介绍关联容器)。

向顺序容器添加元素

不知道为啥原书篇幅超多,我觉得直接看代码就很明了,所以我就写代码了:

list<int> a = {1, 2, 3}; //注释为a的元素内容{1, 2, 3}
a.push_back(4); //{1, 2, 3, 4},array和forwar_list不支持
a.push_front(0); //{0, 1, 2, 3, 4},只有list,forward_list和deque支持

以上两种可以方便地在头尾添加元素,更进一步,insert允许我们在容器任意位置中插入多个元素,vector,deque,list,string都支持insert:每个insert接受一个迭代器作为第一个参数:

list<string> slist = {Jay"};
auto iter = slist.begin();
slist.insert(iter, "Hello"); //将Hello添加到iter之前的位置

//insert还有重载版本
vector<string> svec;
svec.insert(svec.end(), 10, "May"); //在末尾插10个May
vector<string> a = {"1", "2"};
slist.insert(slist.begin(), a.end()-1, a.end()); //插入了"2"
slist.insert(slist.begin(), slist.begin(), slist.end());
//错误,要拷贝的迭代器不能指向自己

现在我要在vector中实现一个功能,每次插入的单词都插在头部:

vector<string> a;
auto it = a.begin();
string word;
while(cin >> word)
{
it = a.insert(it, word);
}

因为insert会返回当前位置,调用insert会在前一个位置插入,相当于调用了push_front

新标准还引入了三个新成员-emplace_front,emplace和emplace_back,这些操作时构造元素,不是拷贝元素,它们仨分别于push_front,insert和push_back对应:

vector<Sales_data> c;
c.emplace_back("1", 25, 16.8);
//在c的末尾构造一个Sales_data对象(调用三个参数的构造函数)

c.emplace_back("1", 25, 16.8); //这样不对的
c.emplace_back(Sales_data("1", 25, 16.8)) //这样可以

访问元素

每个顺序容器都有一个front函数,返回首元素的引用;除forward_list之外的都有一个back,返回尾元素的引用,还是看代码好理解:

vector<string> c = {"1", "2"};
if(!c.empty)
{
auto v1 = *c.begin(), v2 = c.front(); //v1,v2都是首元素的拷贝

auto last = c.end();
auto v3 = *(--last); //除forward_list之外
auto v4 = c.back(); //也除forward_list之外
}

c[n]和c.at(n) 返回下标为n的元素的引用:

if(!c.empty)
{
auto &v = c.back(); //获得尾元素的引用
v = “3”; //改变了c中的元素
auto v2 = c.back();
v2 = "0"; //没改c的元素,是个引用
}

与以前一样,如果用auto变量保存这些函数的返回值,并且要改变容器内的值,必须要将变量定义为引用类型。 下标一定要在合理范围内,这个程序员自己要注意,来个错误示范:

vector<int> a;
cout << a[0]; //错了

删除元素

vector<int> a = {1, 2, 3, 4, 5, 6};
a.pop_front(); //删除首元素
a.pop_back(); //删除尾元素

//删除a中所有奇数
auto it = a.begin();
while(it != a.end())
{
if(*it % 2)
{
it = a.eraser(it); //删除奇数
}
else
{
++it;
}
}

//删除所有元素,两种方式
a.clear();
a.eraser(a.begin(), a.end());

特殊的forward_list操作

全部在下图了,也好记,用了再找也行:
把删除奇数元素的代码用forward_list重写一遍,要注意两个迭代器,一个指向要处理的元素,一个指向它前面那个元素:

forward_list<int> a = {1, 2, 3, 4, 5, 6};

auto pre = a.before_begin(); //首前元素
auto cur = a.begin(); //首元素
while(cur != a.end())
{
if(*it % 2)
{
cur = a.eraser_after(pre); //删除并移动cur
}
else
{
pre = cur; //下一个
++cur;
}
}

改变容器大小

除了array之外,我们可以用resize来改变容器大小:

  • 如果当前大小大于所要求的的大小,容器后面的元素会被删除
  • 如果当前大小小于新大小,会将新元素添加到容器后部
    list<int> a(10, 42); //10个42
    a.resize(15); //后面再加5个0,是默认初始化的,
    //如果是类类型,要么就有默认构造函数,要么就提供初始值
    a.resize(25, -1); //后面再加10个-1
    a.resize(5); //从末尾删除20个元素,就剩5个42
    缩小容器,指向被删除元素的迭代器、引用和指针都会失效;对vector、string和deque进行resize也可能导致它们失效。

容器操作可能使迭代器失效

使用失效的指针、引用或迭代器是很严重的错误,我们来仔细分析一下,分添加和删除两种情况
添加元素:

  • 容器是vector或string:
  • 如果存储空间被重新分配,则指针等全部失效
  • 未重新分配,插入位置之后的那些都失效
    总之就是内存位置变了就不行了
  • 对于deque,插入除了首尾之外的位置就会失效;如果在首尾添加,迭代器失效,引用和指针不会失效
  • 对于list和forward_list,都还是有效的

删除元素:
(被删除的元素对应的那三样肯定挂了)

  • 对于list和forward_list都有效
  • 对于deque,
  • 如果在首尾之外的任何位置删除,都失效;
  • 如果删除尾元素,尾后迭代器失效,其他不影响,如果删除首元素
  • 如果删除首元素,都不受影响
  • 对于vector和string,被删除元素之前的都有效,所以尾后迭代器总会失效
注意函数返回迭代器的位置

我们来写一个函数,删除偶数元素,复制每个奇数元素:

vector<int> vi = {0, 1, 2, 3, 4, 5};
auto iter = vi.begin();
while(iter != vi.end())
{
if(*iter % 2)
{
iter = vi.insert(iter, *iter);
iter += 2; //跳过当前元素和复制元素
}
else
{
iter = vi.eraser(iter); //删除偶数元素
//不用向前移动迭代器,因为eraser返回删除元素的下一个
}
}
不要保存end返回的迭代器
vector<int> vi = {0, 1, 2, 3, 4, 5};
auto begin = vi.begin();
auto end = vi.end();
while(begin != end)
{
begin = v.insert(begin, 42);
++begin; //跳过刚刚加入的元素
}

有问题的,因为end记住了一个以前的end,更好的应该这样:

vector<int> vi = {0, 1, 2, 3, 4, 5};
auto begin = vi.begin();
while(begin != vi.end())
{
begin = v.insert(begin, 42);
++begin; 
}
#C++工程师#
全部评论
slist.insert(slist.begin(), a.end()-1, a.end()); //插入了"1" 这里应该是“1”和“2”
点赞 回复 分享
发布于 2016-12-25 15:44
empty() ?
点赞 回复 分享
发布于 2017-02-11 15:37

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务