通用容器
STL
是一个容器集,容器又是对象的集合(它里面持有对象)
1. 容器和迭代器
容器可以根据它里面对象的需要自行扩展,我们在使用容器时,不需要知道它里面要放多少个对象,也不需要知道容器里的处理细节,只需要定义一个容器对象,然后由容器来处理全部细节。不同的容器它们的接口类型和外部行为有所不同,它们处理相同操作所耗费的效率也不同。譬如:
vector
遍历元素或访问元素很方便,但是要在其中插入元素就很耗效率;而使用
list
要插入一个元素就很方便,但随机访问一个元素却比
vector
效率低。这些操作依赖于这些序列的底层结构。不同容器的关键不同之处在于它们在内存中存储对象的方式和向用户提供的操作。当我们使用时可以根据不同的需进行选择。
为了灵活的访问容器中的元素,引入迭代器
(iterator)
。迭代器是为了实现通用而做的抽象。使用迭代器可以返回迭代器所指的元素给迭代器的使用者。它可以灵活的遍历容器中的每一个元素。也容许同时存在多重迭代器。通过迭代器,容器可以被看作一个序列。迭代器操作与容器的操作是分离开的,任何一方的变动都不会影响到另一方。通过迭代器可以读取元素,也可以给元素赋值(改变元素值)。
2. 概述
容器分为三大类,每一类有分为几个类型:
分类
|
容器
|
序列容器
|
vector, list, deque
|
适配器容器
|
queue, stack, priority_queue
|
关联式容器
|
set, map, multiset, multimap
|
序列容器只是将它们的元素线性地组织起来,是最基本的容器类型。
适配器容器可以在序列容器需要的时候为它们附上某些特殊的属性(如:队列或栈的抽象建立模型)。
关联式容器则是基于关键字来组织它们的数据,并允许快速的检索那些数据。
适配器容器可以在序列容器需要的时候为它们附上某些特殊的属性(如:队列或栈的抽象建立模型)。
关联式容器则是基于关键字来组织它们的数据,并允许快速的检索那些数据。
vector
、
list
和
deque
的区别
:
1)
vector
是一个允许快速随机访问其中元素的线性序列,它随机访问元素的速度快,但是向其中插入元素的速度慢,不支持
push_front()
操作;
list 是一个双向链表,要移动它的元素所需花费的代价很高,但是很容易向其中任何地方插入元素;
deque 是一个双端队列,也可以用几乎和 vector 一样快 ( 但是会比 vector 稍慢 ) 的速度随机访问其中的元素,但是它扩展资源的速度要快很多,而且可以很容易的在它两端加入新元素。
list 是一个双向链表,要移动它的元素所需花费的代价很高,但是很容易向其中任何地方插入元素;
deque 是一个双端队列,也可以用几乎和 vector 一样快 ( 但是会比 vector 稍慢 ) 的速度随机访问其中的元素,但是它扩展资源的速度要快很多,而且可以很容易的在它两端加入新元素。
2)
vector
和
deque
允许使用检索操作符
[
]
,但
list
不允许。
3)
它们都提供了相应的类型的迭代器来访问它的元素。
set
里面存入的是不重复的元素,且放入这些元素时会自动对它们排序。
容器中所持有的是存入对象的拷贝,并根据需要扩展它们的资源。所以这些对象必须有可访问的拷贝构造函数和可访问的赋值操作符。对于在容器里所存对象堆空间的分配管理需要用户自己做。譬如:当容器所持有的对象被销毁之后,容器并不会自动
destroy
它所持有的相应指,而需用户自己去做。对于解决这种问题,最容易和最安全的方法就是使用智能指针
(smart pointer)
。
同一个对象,可以有不至一个容器里的指针指向它。
1) 字符串容器
2) 从STL容器继承
继承自容器的类的对象 也具有那个容器的属性和行为。
3. 更多迭代器
<ContainerType>::iterator;
<ContainerType>::const_iterator
如果一个容器是
const
(常)容器,那么它的迭代器也是
const(
常
)
迭代器,也就是不允许更换这些迭代器所指向的元素(因为相应的运算符都是
const
的)。
所有迭代器都++可以前向移动,也可以使用
!=
或
==
对迭代器进行比较。
可以通过使用解析运算符
(operator *)
来取得迭代器当前所指向的容器元素。
如果
it
是一个可遍历容器的迭代器,且
f()
是该容器所持有的对象的成员函数,那么可以用
(*it).f()
或
it->f()
访问(
(
容器所包含的类型的
)
对象的)这个成员函数。但是要注意如下用法:
template
<class Cont, class PtrMemFun>
void
apply(Cont& c, PtrMemFun f) {
typename Cont::iterator it = c.begin();
while(it != c.end())
{
//(it->*f)();//
将当成it使用操作符->* ,但是在迭代器类中并没有提供->* 这个操作符。
//(it->(*f))();//error C2039: 'it' : is not a member of 'Z'
((*it).*f)(); // Alternate form
++it;
}
}
1) 可逆容器中的迭代器
可逆容器的迭代器可用来反向遍历容器,有成员函数
rbegin()
用于产生一个选择了容器末尾的迭代器
reverse_iterator
,
rend()
用于产生一个选择了容器超越起始的迭代器
reverse_iterator
。对所有的容器调用
rbegin()
和
rend()
可得到
reverse_iterator
对象。如果容器是
const
的,那么
rbegin()
和
rend()
产生的迭代器是
const_reverse_iterator
。
2) 迭代器的种类
为什么要对迭代器划分种类?因为:一使用某些内置的迭代器类型或创建自己的迭代器时,迭代器的种类就很重要;二使用
STL
算法时,每种算法对其迭代器都有使用场合的要求。在创建用户自己的可重用的算法模板时,这些种类知识尤其重要,因为自定义的算法所需要的迭代器种类决定了该算法的灵活性。如果只要求最基本的迭代器类型(输入或输出迭代器),则这种算法可以适用于任何场合(如
copy()
)。
一个迭代器类的种类由一个迭代器的层次结构标记类进行标识,类名和迭代器的种类相符合,且它们之前的派生层次结构反映了它们之间的关系:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag{} : public input_iterator_tag{};
struct bidirectional _iterator_tag{} : public forward_iterator_tag{};
struct random_access_iterator_tag{} : struct bidirectional_iterator_tag{};
(
注:因为前向迭代器
forward_iterator
需要使用超越末尾的迭代器值,而输出迭代器
output_iterator
总是假定它总是可以解析的,可是保证不把一个超越末尾的迭代器值传给一个需要使输出迭代器的算法中是很重要的,所以,
forward_iterator
只从
input_iterator
继承而不继承
output_iterator.)
为了提高效率,有些算法为不同的迭代器提代不同的实现。
下面按行为功能由严格到最强大的顺序来分别讨论看看具体划分为哪些种类以及它们分别有什么性能:
输入迭代器:
只读,一次传递
可以对读操作的结果进行解析(一个值只解析一次),然后前向移动,可以用与超越末尾的值比较判断是否结束。为输入迭代器的预定义实现只有
istream_iterator
和
istreambuf_iterator
,用于从一个输入流
istream
中读取。
输出迭代器:
只写,一次传递
可以对写操作的结果进行解析(一个值只解析一次),然后前向移动,没有使用超越末尾来的值结束的概念。为输出迭代器的预定义实现只有
ostream_iterator
和
ostreambuf_iterator
,用于从一个输出流
ostream
中读取。
前向迭代器:
多次读
/
写
前向迭代器包括输入
/
输出迭代器的所有功能,不同的是它可以对一个迭代器所指定位置多次解析(也就是可以对迭代器所指向的值多次读写)。它也是只能前向移动。没有专为前向迭代器预定义的迭代器。
双向迭代器:
operator--
它具有前向迭代器的所有功能,另外还具有后向移动的功能。由
list
返回的迭代器都是双向的。
随机访问迭代器:
类似于一个指针
它具有双向迭代器的所有工轻盈,再加上一个指针所具有的功能,除了不具有一种空
(null)
迭代器和空指针对应。它可以像一个指针一样进行任何操作,可以使用
operator[ ]
进行索引,可以加若干值指向另一位置(向前
/
后移动),可以运用比较操作符在迭代器之间进行比较。
3) 预定义迭代器
front_inserter()
用一个容器对象作参数,产生一个调用
push_front
进行赋值的迭代器
back_inserter()
用一个容器对象作参数,产生一个调用
push_back
进行赋值的迭代器
.
inserter()
是插入而不是压入一个元素,再次替代
operator=,
它在插入之前需要知道要插入位置的迭代器
如:
int a[] = { 1, 3, 5, 7, 11, 13, 17, 19, 23 };
deque<int> di;
vector<int> vi;//
不支持
push_front
和
push_back.
copy(a, a + sizeof(a)/sizeof(deque), front_inserter (di));
//di{ 23, 19,17, 13, 11, 7, 5, 3, 1}
di.clear();//
清空
di
copy(a, a + sizeof(a)/sizeof(deque),back_inserter(di));
//di{1, 3, 5, 7, 11, 13, 17, 19, 23 }
copy(a,a + sizeof(a)/(sizeof(Cont::value_type)*2),inserter(ci,it));
//di{1,3,5,1,3,5,7,7,11,13,17,19,23}
l
更多的流迭代器
输入流迭代器
istream_iterator
有两个构造函数,一个获得输入流
istream
并且产生一个实际读取的迭代器对象,另一个是默认构造函数,用于产生一个作为超越末尾标记的迭代器对象。
输入流迭代器会丢失一些空格、回车、
tab
之类的空白字符,所以平时使用更多的是输入输出流缓冲迭代器,除非你不用考虑空白字符是否会丢失的问题。另外,
istream::operator>>
每次操作会增加较多开销。
l
操作从未初始化的存储区
raw_storage_iterator <tn1, tn2>
是一个输出迭代器,可以用来操作操作未初始化的存储区。
它有两个模板参数:
tn1
是输出迭代器类型,
tn2
是所存储对象类型,输出迭代器指向这块未初始化的存储区。它提供的算法使结果存放到未经初始化的内存区。
接口:它的构造函数有一个指向原始(某未初始化)内存储区的迭代器(典型的指针),
operator=
将一个对象分配给那个未初始化的原始内存。
注意:原始内存区类型必须与所创建的对象类型相同(可以在创建时进行强制类型转换)。必须显示调用析构函数进行清理工作,也允许在操作容器期间每次删除一个对象。
Delete
表达式中的静态指针类型必须与
new
表达式中分配的类型相同。
4. 基本序列容器:vector、list和deque
所有基本序列容器都是完全按照存入去时的顺序持有对象。但是,不同的容器进行某些操作时的效率是不同,所以,我们在使用时要根据不同的操作需要选择合适的容器。
1) 基本序列容器的操作概述
都是可逆容器。
有多个构造函数,用默认构造函数创建的容器对象是空的;
可以使用赋值成员函数
operator=
和两种类型的
assign()
(
assign(it1,it2); assign(value_count,value);
)对容器赋值;
可以用
resize()
重置容器大小;
可以用
insert()
在容器中插入一个或一组元素;
可以使用
resverse_iterator
反向读取容器中的元素;
可以用
erase()
的两个不同版本来清除序更中间的一个元素或一组元素;
可以用
clear()
来清空容器。
可以用++或——把
iterator
前向或后向移动一个位置,因为
vector
和
deque
可以产生随机访问迭代器,所以它们还可以使用
operator+
或
operator-
来一次移多个位置,但
list
就只能一次移一个位置。
它们三个都支持
push_back()
或
pop_back()
。
list
和
deque
还支持
push_front()
和
pop_front()
,但
vector
不支持
push_front()
和
pop_front()
。
成员函数
swap()
可以相互交换两个
(
持有相同类型对象的
)
容器的所有东西,也就高效的交换了容器本身。还有可用于交换两元素的非成员函数
swap()
,还有用于通于迭代器交换同一容器内两个元素的
iter_swap
算法。
下面将分别具体讨各类型序列容器的特点。
2) 向量(vector)
vector
模板有点像数组,具有数组风格的快速索引,还可以动态地扩展。它可以快速索引和迭代,也可以在最后一个元素之后新增加元素,但不能在中间或前面插入元素。
l
已分配存储区溢出引起的问题
当新增元素而存储空间不够时,就会
(1)
在其它位置重新分配更大的新存储空间,
(2)
用拷贝构造函数把原位置的所有元素都拷贝到新的存储空间中,
(3)
调用析构函数把原位置的元素销毁,
(4)
释放原位置的内存。这样会引起一些副作用。
可以用
reserve()
为
vector
预先分配足够大的空间,但是它与用
vector
构造函数的第一个参数来指定分配空间大小的做法是有区别的,后者是使用元素类型的默认构造函数来初始化元素
,
而
reserve()
只是预分配空间,但并没调用构造函数初始化,且用
size()
取得值为0,也就是仍是空容器。有些操作会引起迭代器无效,譬如
resize()
。在进行一些迭代器无效的操作之后,如果要使用迭代器,需重新置迭代器。
由此可见,选择使用
“vector”
的最安全的方法是:一次性填入所有元素,然后在程序的另一处只使用它而不再加入新元素。
l
插入和删除元素
使用
vector
最有效的条件是:
1)
在开始时用
reserver()
分配了正确数量的存储构,
vector
绝不再重新分配存储区;
2)
仅仅在序列的后端添加或者删除元素。
3) 双端队列(deque)
deque
与
vector
的区别就在于:
vector
的存储区必须是连续的,而
deque
的存储区不必是连续的存储块;
deque
也有
vector
的所有操作,比
vector
多了
push_front
和
pop_front
操作;
在随机访问元素时
deque
比
vector
稍慢,但是在插入元素需要重新分配存储区时
deque
不需要复制并销毁原有元素,插入和删除元素
deque
都比
vector
有效率的多。
已配置存储区溢出的引起的问题
与
vector
不同,当
deque
所用存储区不够用时,它会根据当前缺少就只分配多少的一个新存储区,原有的存储区和元素都不变,也就不会有额外的拷贝构造和析构发生,不过它用于保存数据块索引信息的存储区有可能需要重新分配。在
deque
中间插入元素比
vector
更麻烦,但代价不大。
因为
deque
的存储管理方式,在
deque
的两端添加元素,不会引起现有迭代器失效。
再次说
vector
用于预先知道容器中要存入的元素数目的情况比较合适。
当不确定将被存入容器中的元素数目时,用
deque
优于
vector
。
只需很小改动即可以把
vector
转用
deque
。
总之,在以下情况下使用
deque
是最好的:
1)
从序列的两端插入或删除元素;
2)
合理的快速遍历容器元素;
3)
使用
operator[ ]
相当快速的随机访问。
4) 序列容器间的转换
序列容器都有一个由两个迭代器作参数的双参数构造函数和一个将数据读入现存容器中的
assign()
成员函数,用它们来从一个序列容器转换为另一个序列容器是很容易的。它只是把那些对象拷贝构造到新容器中。
5) 被检查的随机访问
vector
和
deque
都提供了两个随机访问函数:索引操作符
operator[ ]
和判定是否到了容器边界的的函数
at()
,是边界则返回
true
,否则返回
false
,超出边界则抛出一个异常。但是用
at()
的代价比
operator[ ]
的代价大。
6) 链表(list)
list
是一个以双向链表数据结构实现的序列容器。
如果有较大、复杂的对象,就首选
list
,特别是如果构造、拷贝、赋值、析构操作的代价大,如果进行大量的譬如对象排序或以别的方式进行重新排列操作是更是这样。
list
可以在序列的任何地方快速的插入或删除元素,但是代价较高,所以适插入或删除较大对象的元素;没有
operator[ ]
,所以它随机访问元素非常慢,适合的情况就是从头到尾或从尾到头顺序遍历元素而不是随机访问一个中间元素,但还是很慢。除非已知要访问元素附近的一个迭代器,否则它的遍历都是从头或尾开始。
list
容器中的元素在创建以后绝对不会移动,即使插入或删除一个元素(这时只会改变它的链接关系),也不会拷贝构造或向某个实际对象赋值,所以向
list
中添加元素,已有迭代器不会失效。
对于只需要改变链接而不移动对象的操作(如逆转、排序),不会进行拷贝对象。
Swap
就是通过拷贝来进行两个元素的交换。
一般来说如果系统提供了一个算法的成员版本就采用它的成员版本而不用其等价的通用的算法。通用的
sort()
和
reverse()
算法只适用于数组、
vector
和
deque
。
l
特殊的
list
操作
list1.
splice
(it,list2);//
把整个
list2
插入到
list1
的
it
位置处,并删除
list2
源链表中所有对象元素
list1
.splice
(it1,list2,it2);//
把
list2
中
it2
处的元素插入到
list1
的
it
处
,
同时删除源链表中该元素的值
list1.
splice
(it1,list2,it2,it3);//
把
list2
中的
it2
到
it3
之间
([it2,it3])
的元素插入到
list1
的
it1
处
remove
(it) //
删除
list
中所有与
it
处元素等值的元素
,
删除操作链表不必排序
list.
merge
(list1);//
把
list
与
list1
合并并排序,合并后将
list1
源链表已被删除
(
因为它们已移到新链表中
)
,合并前要先分别对它们用
sort()
排序
unique
() //
删除
list
中相邻的重复的元素,须先对
list
用
sort
排序然后再
unique()
才有效
l
链表与集合
执行
list
的
unique
操作之后,
list
变成了一个
set
。
也就是
list
的
unique()
和
set()
都可以保证容器中的元素不重复,但
set()
更高效一些
7) 交换序列
成员函数
swap()
用于同类型序列的相互交换。它是高效率的。它的执行不需进行拷贝和赋值,不论交换的两同类型的序列长度是否相等。实际上它们执行的时它们两个资源的相互交换。
STL
也包括一个
swap()
算法,它用在两相同类型的容器交换时,它具有很快速的性能。所以,对容器的容器用
sort
算法排序,也是很快速的。
5. 集合set
set
只接受每个元素的一个副本。
set
中的元素是用
operator<
进行排序的。想创建一个容器放入元素时就自动排序,用
set
是很好的选择。
Set
的
find()
也很快速,比通用的
find()
算法要快很多,因为对已排好序的序列容器在查找元素时,用
equal_range()
就可以得到对数级的算法复杂性。
6. 堆栈stack
堆栈
stack
与
queue
和
priority_queue
一起被归类为适配器容器。它们通过调整一个基本序列容器以存储自己的数据。
Stack
类的
pop()
并不返回栈顶元素,而是返回一个
void
值。如果要取得栈顶元素,可以用
top()
来得一个它的引用。
Stack
没有提供迭代器,也没有初始化形式,只提供了一个简单的接口。
7. 队列 queue
队列也是一个适配器容器,也是建立在一个基本序列容器之上,它的默认模板参数是
deque
,
deque
是
queue
的理想的实现。它是受限的
deque
,它只能在一端插入元素,在另一端删除元素。在需要使用
queue
的任何地方使用
deque
,那样也可以使用
deque
的附加功能。当强调只使用
queue
相似的行为的时候,使用
queue
而不用
deque
。
8. 优先队列 priority_queue
优先队列也是一个适配器容器,也是基于基本序列容器进行构建的适配器,默认的序列容器是
vector
。
优先队列拥有与
stack
几乎相同的接口,但是这的表现不同。优先队列的栈顶元素是具有优先级最高的元素。
不能在一个
priority_queue
上从头到尾迭代,但可以用一个
vector
来模拟
priority_queue
的行为,因此允许访问那个
vector
。
它使用的函数有:
make_heap()
、
push_heap()
、
pop_heap()
。
可以说,堆就是一个优先队列,
priority_queue
是对堆的一个封装。
make_heap() & sort_heap()
9. 持有二进制位
表示二进制的两个类:
bitset
和
vector<bool>
。它们都不是传统的“
STL
容器”。
bitset
和
vector<bool>
的区别:
1)
bitset
是持有固定数目的二进制位;
vector<bool>
持有的二进制位数目可以扩展。
2)
bitset
模板是为了在操纵二进制位时提高性能而设计的,它不是正常的
STL
容器,没有迭代器,它允许底层的整型数组存储在运行时的栈上,它有一个面向二进制位层次的操作接口,绝不与前面所讨论的
STL
容器相似。
vector<bool>
是
vector
容器的一个特化,它的设计是用来提高
bool
数据的空间使用率,有普通
vector
的所有操作。
1) bitset<n>
一些相关操作:
to_ulong
把二进制位转化成
usigned long
数字
cbits(string)
cbits(string,int)
cbits(string,int,int)
test(i)
测试第
i
位是否为1
set()
置所有位为1
,set(i)
置第
i
位为1
reset()
置0,
reset(i)
将第
i
位置0
flip()
将所有位取反,
flip(i)
将第
i
位取反
count()
有多少个
1
any()
有
(true)
没有
(false)1
none()
是
(true)
否
(false)
全部为
0
可以使用索引
operator[ ]
2) vector<bool>
它的迭代器必须特殊定义,不能使用指向
bool
的指针
它的操作函数比
bitset
受更多的限制,它比普通的
vector
多一个操作
flip()
就是对所有位取反。
使用
operator[ ]
时返回一个
vector<bool>::reference
类型的对象,这个对象也有一个用于对个别位取反的成员函数
flip()
。
// Convert to a bitset:
将一个
vector<bool>
转换成
bitset<>
时,可以把
vector<bool>
转换成
string
,再转换成一个
bitset<>
ostringstream os;
copy(vb.begin(), vb.end(),ostream_iterator<bool>(os, ""));
bitset<10> bs(os.str());
10. 关联式容器
关联式容器有:
set
、
map
、
multimap
。因为它们将关键字和值关联在一起,至少
map
和
multimap
是这样子的。
Set
可以看作是没有值的
map
,它只有关键字,
multiset
与
mutipmap
的关系也是这样。它们的结构是相似的。
它们主要的操作就是把对象放入容器。当把对象放入容器时,
set
会检查是否集合中已有这个对象;
map
会检查是否已有这个关键字,如果有就把这个值关联给关键字。
isert()
cunt()
是否有元素
fnd()
返回这个元素第一次出现的
iterator
只对
multimap
和
multiset
有意义
:
lwer_bound()
uper_bound()
eual_range()
在
map
中将索引设置为一个超出范围的值,意味着要创建一个新条目,如果使用
operator[ ]
来查找一个条目,当不存在这个条目时
map
会新创建一个新条目,所以一般用
count()
、
find()
查找。
1) 用于关联式容器的发生器和填充器
在序列容器中用
fill()
、
fill_n()
、
generate()
、
generate_n()
填充数据时,其实现是用赋值方式
operator=
将值放进序列,对关联式容器填充数据时,是用各自的
insert()
函数来实现。
2) 不可思议的映像
用
map
的迭代器解析得到的是一个
pair
(也就是称为
value_type
的对象),可以用
first
得到它的键值,用
second
得到它的键值。用
insert()
插入时也是先构建一个
pair
,然后把这个
pair
插入
map
。
3) 多重映像和重复的关键字
对
multimap
里面会有重复值,这时可以用
equal_range()
成员函数,它返回这些重复值
pair
集的起始迭代器和结超出范围的迭代器
4) 多重集合
Mutipset
里可以有重复的元素值,但是这些重复值是一定连续的排在一起。同
multimap
一样可以用
equal_range()
返回这些重复值的起始和超出范围的迭代器,还可以用
distance()
来得到这个范围有多大。
也可以取得有哪些值是唯一存在的。
如果想不受限制的存放一个字符串,可以使用
vector/deque/list
。
11. 将STL容器联合使用
有时单独使用任一种类容器都不合适,那么可以把几种容器联合起来使用。譬如可以创建
vector
的
map
,而
vector
又包含
map
。
12. 清除容器的指针
容器中的指针不会自动清除,需要我们手动清除,但是,也要保证不要对多个容器中持有的指向同一对象的指针进行多次清除,也就是不要对一个对象多次清除。但是对一个容器中的指针清除后赋为0,即使对这些指针多次清除也不会有问题。
13. 创建自己的容器
有了
STL
作基础,用户就也可以创建自己的容器了。
14. 对STL的扩充
尽管
STL
已提供了需要的很多功能,但它们还不是完美的。譬如
set
和
map
的速度虽然很快但有时还是不能满足用户的需要。
SGI STL
增加很多扩充的容器,包括
hash_map
、
hash_set
、
hash_multimap
、
hash_multiset
、
slist(
单链表
)
、
rope(
一个
string
的变种,对非常大型的字符串、字符串的快速连结、取子字符串等操作进行了优化
)
。
hash_map
要比
map
的某些操作的性能好些。
在
map
和
hash_map
中
find()
要比
operator[ ]
的操作稍快些。
15. 非STL容器
在标准库中有两种“非
STL
”容器:
bitset
和
valarry
。因它们没有完全符合
STL
的要求而被称为非
STL
容器。它们都没有迭代器。
bitset
把二进制位打包成整数且不允许对其成员进行直接寻址。
valarry
是一个类
-vector
的模板类,它对一些有效率的数值计算进行了优化。
Valarry
提供了一个构造函数,该构造函数接受一个目标数组类型的参数和数组中元素计数的参数来初始化一个新的
valarry
。成员函数
shift()
将元素左移一个位置,移走后该位置的空位置填元素类型的默认值,如果参数是负的表是向右移;
cshift()
是进行循环移动;二进位运算符要求
valarry
具有相同大小和类型的参数;成员函数
apply()
对每一个元素应用一个成员函数,但结果被收集到一个结果
valarry
中;关系运算符返回大小匹配的
valarry<bool>
实例,该实例显示了元素与元素逐个对比的结果。它的大多数操作返回一个数组,但也有个别的返回一个数值,如:
min()
、
max()
、
sum()
。对
valarry
可以引用其元素中的一个子集(这个子集也被称为切片
slice
)。某些运算也可以用切片来做它们的工作。一个切片接受三个参数:起始索引、要提取的元素合计数和“跨距”(也就是用户感兴趣的两个元素之间的间距)。切片可以作为一个现有
valarry
的索引,并返回一个包含被提取元素的新的
valarry
。