C++程序设计:原理与实践(进阶篇)15.5 再次泛化vector

简介:

15.5 再次泛化vector


显然,通过15.3~15.4节的例子我们发现,标准库vector包含一个iterator成员类型,以及begin()和end()成员函数(与std::list类似)。然而,我们并没有在第14章中为vector类提供这些成员。那么,对于不同类型的容器而言,它们究竟采用了什么方法,以使它们或多或少地能够在15.3节所介绍的STL泛型编程风格中相互替换使用?首先,我们将简要介绍一种解决方案(简单起见,我们忽略了分配器),然后再对解决方案进行解释:

 

 

 

using声明为一个类型创建别名,即对于我们的vector,iterator是我们用作迭代器类型T*的一个同义词,它的另一个名字。现在,对于vector对象v,我们可以编写如下代码:

 

以及

 

通过别名的方式,我们事实上不需要知道iterator和size_type的实际类型。特别地,由于使用了iteartor和size_type,上述代码也可以用于那些size_type不是unsigned long类型(在很多嵌入式系统中,size_type为其他类型)并且iterator为类而不是简单指针(这种情况在C++实现中很普遍)的vector。

标准库以相似的方式定义了list和其他标准容器。例如:

 

 

这样,我们编写代码时就不必操心使用的是list还是vector。所有标准库算法中都使用了上述这些容器的成员类型名,例如iterator和size_type,所以,算法的实现不依赖于容器的实现或者具体操作的是什么容器(参见第16章)。

还有一种方法可以替代对容器C使用C::iterator——Iterator<C>,这也是我们通常更倾向使用的方法。通过一个简单的模板别名即可实现这种用法:

 

出于语言技术层面的原因,我们需要为C::iterator加上typename前缀,以表明iterator是一个类型,这也是我们更倾向于使用Iterator<C>的原因之一。类似地,我们可以定义

 

这样,我们就可以在代码中使用Value_type<C>了。这些类型别名不包含在标准库中,但你可以在std_lib_facilities.h中找到它们。

using声明是C++11的新特性,与C和C++中为人熟知的typedef(见附录A.16)相似,可以看作后者的泛化。

15.5.1 遍历容器

使用size(),我们可以从头到尾遍历一个vector。例如:

 

这段代码不适用于链表,因为list不提供下标操作。但是,我们可以用一个简单的范围for循环(见4.6.1节)来遍历标准库vector和list。例如:

 

这段代码既适用于标准库容器,也适用于“我们的”vector和list。它是如何办到的?“窍门”在于范围for循环是基于begin()和end()函数的,前者返回指向我们的vector的首元素的迭代器,后者返回指向尾元素之后位置的迭代器。范围for循环其实不过是使用迭代器遍历序列的循环的一种“语法糖衣”而已。如果我们为自己的vector和list定义了begin()和end(),就“偶然地”提供了范围-for所需要的东西。

15.5.2 auto

当我们不得不编写循环遍历一个通用结构时,命名迭代器是很令人厌烦的事情。考虑下面代码:

 

这里最恼人的地方是编译器显然已经知道了list的iterator类型和vector的size_type。我们为什么还必须告诉编译器它已经知道的事情呢?这样做徒增我们当中不擅打字的人的烦恼,并增加出错的机会。幸运的是,我们无须这样做:可以将变量声明为auto的,表示使用iterator类型作为变量的类型:

 

这里,p是一个vector<T>::iterator,q是一个list<T>::iterator。我们几乎可以在任何包含初始化器的定义中使用auto。例如:

 

注意,字符串字面值常量的类型为const char*,因此对字符串字面值常量使用auto可能导致令人不快的意外:

 

当我们确切知道想要的类型时,通常指明类型和使用auto一样容易。

auto的常见用途是在范围for循环中指明循环变量。考虑下面代码:

 

在这段代码中,我们使用auto的原因是给出容器cont的元素类型不是那么容易。我们使用const的原因是并不写入容器元素,而我们使用&(引用)的原因是以免元素过大拷贝代价太高。

相关文章
|
11天前
|
小程序 编译器 Linux
C++ 异常原理:以一个小程序为例
作者在调查某个 bug 时涉及到 C++ 异常,借此机会以本文把 C++ 异常机制梳理清楚供大家参考。
|
1天前
|
编译器 C++ Windows
【C++】vector问题解决(非法的间接寻址,迭代器失效 , memcpy拷贝问题)
不使用memcpy函数不就可以了,然后我们使用简单粗暴的赋值拷贝,这样就不会发生浅拷贝问题了!!!
12 1
|
1天前
|
存储 C++ 容器
【C++】vector容器初步模拟
我们初步完成了对vector 的模拟实现,但是依然有问题,比如不支持string等特殊类型。所以下一篇文章我们来一起完善一下。
7 0
|
4天前
|
算法 C++ 容器
【C++/STL】vector(常见接口、模拟实现、迭代器失效)
【C++/STL】vector(常见接口、模拟实现、迭代器失效)
9 0
|
11天前
|
Linux 程序员 图形学
C++语言在现代软件开发中的应用与实践
C++语言在现代软件开发中的应用与实践
19 2
|
11天前
|
存储 程序员 C语言
深入理解C++:从语言特性到实践应用
深入理解C++:从语言特性到实践应用
22 3
|
11天前
|
存储 算法 安全
C++语言深度探索:从基础到实践
C++语言深度探索:从基础到实践
13 2
|
11天前
|
算法 程序员 C语言
C++:深度探索与编程实践
C++:深度探索与编程实践
15 3
|
3天前
|
设计模式 安全 算法
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
【C++入门到精通】特殊类的设计 | 单例模式 [ C++入门 ]
14 0
|
4天前
|
C语言 C++
【C++】string类(常用接口)
【C++】string类(常用接口)
13 1

热门文章

最新文章