::std::vector<> 的存储管理

以下成员函数用于存储管理:

void reserve( size_t n );

size_t capacity() const;

void resize( size_t n, T t=T() );

void clear();

size_t size() const;

bool empty() const { return size() == 0; }

size_t max_size() const;

另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。

1) max_size()

返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字,大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。

2) size()

返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。

3) empty()

如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。

4) clear();

清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T 可被缺省构造。相当于 CArray<>::RemoveAll()。

5) resize( size_t n, T t = T() );

将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector 中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加
n - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t ) 后,(size() == n) 成立。

请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。

6) reserve( size_t n );

事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。

7) capacity();

返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后 vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。

vector 管理的动态存储空间是连续的。执行操作

IntVector v(7, 1); // seven ones.

v.reserve( 12 );

后,v 的状态可以用下图表示:

/--size()---\

|1|1|1|1|1|1|1|-|-|-|-|-|

\--capacity()---------/

其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int 类型的对象,但还没有构造的原始空间。再执行

v.push_back( 2 );

v.push_back( 3 );

后,v 的状态可用下图表示:

/----size()-----\

|1|1|1|1|1|1|1|2|3|-|-|-|

\----capacity()-------/

执行 resize( 11, 4 ); 后:

/----size()---------\

|1|1|1|1|1|1|1|2|3|4|4|-|

\----capacity()-------/

capacity() >= size() 总是成立的。对于下标为 [size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:

v[11] = 5; // undefined behavior - anything can happen.

7. 添加元素到 vector 中

下列操作添加元素到 vector 中,并可能引起存储分配:

void push_back( T const& t );

void insert( iterator pos, T const& t=T() );

void insert( iterator pos, size_t n, T const& t );

template<typename Iter>

void insert( iterator pos, Iter first, Iter last );

push_back() 是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last 结束的一个序列插入到 pos 指示的位置之前。

当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector 将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。

这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。

IntVector v;

// add 0, 1, ..., 99 to v:

for( int i = 0; i < 100; ++i )

v.push_back( i );

// append 9, 8, 7,..., 0 to the end:

int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };

v.insert( v.end(), a, a + 10 );

8. 删除元素

下列成员函数完成元素删除:

void erase( iterator );

void erase( iterator first, iterator last );

void pop_back();

void clear();

这些函数分别删除一个,一串,最后一个,或全部元素。

IntVector v;

for( int i = 0; i < 100; ++i )

v.push_back( i );

// 删除 50, 51, ..., 89:

v.erase( v.begin() + 50, v.end() - 10 );

// 删除 49, 48:

v.pop_back();

v.pop_back();

// 全部删除:

v.clear();

注意,删除操作不会引起存储分配,因此 capacity() 不变。

9. 作为序列访问 vector 中的元素

序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在“序列”这个概念之上的。

“序列”是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last 是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last 指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。

叠代子是传统的 C/C++ 中指针的抽象和进一步分类。在 C++ 中把 iterator划分为 input iterator, output iterator, forward iterator,bidirectional iterator, random access iterator 五类。其中的 randomaccess iterator 是最强的一类,即允许的操作最多。C++ 中的指针类型以及vector<>/deque<> 的 iterator/const_iterator/reverse_iterator/const_reverse_iterator 都满足 random access iterator 的要求。

vector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:

iterator begin();

iterator end();

const_iterator begin() const;

const_iterator end() const;

reverse_iterator rbegin();

reverse_iterator rend();

const_reverse_iterator rbegin() const;

const_reverse_iterator rend() const;

这里我们不讨论叠代子的一般概念,只举几个 random access iterator 的例子:

int a[] = { 1, 2, 3, 4, 5, 6 };

[a, a + 6) 是一个随机访问序列,指示了 a[] 中的所有元素。这里叠代子的类型为 int*。

[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。

IntVector v( 100, 1 ); // 100 个 1。

[v.begin(), v.end()) 是一个随机访问序列,指示了 v 中的所有元素,叠代子的类型是 IntVector::iterator。

[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。

[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end() ) 不同,这个序列是从尾到头遍历所有元素。

[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20 )指示的元素相同,但遍历顺序相反。

下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:

begin() ----------> end()

|                   |

v                   v

|0|1|2|3|4|5|6|7|8|9|

^                   ^

|                   |

rend() <---------- rbegin()

IntVector v;

for( int i = 0; i < 10; ++i )

v.push_back( i );

// print 0, 1, 2, ..., 9:

for( IntVector::iterator i = v.begin(); i != v.end(); ++i )

::std::cout << *i << '\n';

// print 9, 8, ..., 0:

for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i )

::std::cout << *i << '\n';

除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector 管理的空间是连续的,因此可以直接取地址进行处理:

::std::vector<HANDLE> handles;

handles.push_back( handle1 );

handles.push_back( handle2 );

::WaitForMultipleObjects(handles.size(), &handles[0],TRUE, INFINITE);

这在与 C 库函数接口时尤其有用。

10. 赋值和交换

vector<> 是可以赋值的,这也是一般的“值”类型必须提供的操作:

IntVector v( 100, 123 );

IntVector v1;

v1 = v;

vector 另外还提供了

template<typename Iter>

void assign( Iter first, Iter last );

void assign( size_t n, T const& t = T() );

用于赋值:

int a[] = { 1, 3, 5, 7 };

v.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.

v.assign( 100 ); // 100 个 0。

还有一个很重要的操作:

void swap( vector& v ) throw();

用于交换两个同类型的 vector 的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。

事实上,swap() 基本上已经被当作类似于 operator=() 的一个“值”类型应该提供的基本操作,::std::swap() 也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:

struct MyVal

{

// blah blah.

void swap( MyVal& ) throw();

};

namespace std {

template<>

void swap( MyVal& a, MyVal& b )

{ a.swap( b ); }

}

关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。

11. 使用 vector 时的存储管理策略

从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。 如果应用程序事先知道要用到的元素个数,可以先调用 reserve() 来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:

IntVector v;

v.reserve( 100 );

for( int i = 0; i < 100; ++i )

v.push_back( i );

请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n) 保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:

IntVector v;

v.resize( 100 ); // v 已经包含 100 个 0.

for( int i = 0; i < 100; ++i )

v[i] = i; // 可以赋值

有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小 vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<> 并没有提供相应的函数。这时必须进行复制:

IntVector(v).swap( v );

有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是

IntVector(v.begin(),v.end()).swap(v);

如果一个 vector 中可能要存储的元素个数较多(例如,超过100个),而且事先无法确定其个数(因此无法调用 reserve()),那么通常 vector 不是一个恰当的数据结构,应该考虑用 ::std::deque<>。与 vector<> 相比,deque<>不保证背后的存储空间是连续的(因此象上面的WaitForMultipleObjects()中的应用不能用 deque<HANDLE> 代替),但有较好的伸缩性,还可以在数组的前端用 push_front()/pop_front() 增减元素(hence its name, doubly endedqueue)。

vector relation的更多相关文章

  1. In abstract algebra, a congruence relation (or simply congruence) is an equivalence relation on an algebraic structure (such as a group, ring, or vector space) that is compatible with the structure in

    https://en.wikipedia.org/wiki/Congruence_relation In abstract algebra, a congruence relation (or sim ...

  2. Gym 100851G Generators (vector+鸽笼原理)

    Problem G. Generators Input file: generators.in Output file: generators.outLittle Roman is studying li ...

  3. Vector Math for 3D Computer Graphics (Bradley Kjell 著)

    https://chortle.ccsu.edu/VectorLessons/index.html Chapter0 Points and Lines (已看) Chapter1 Vectors, P ...

  4. What is an intuitive explanation of the relation between PCA and SVD?

    What is an intuitive explanation of the relation between PCA and SVD? 36 FOLLOWERS Last asked: 30 Se ...

  5. Paper Reading: Relation Networks for Object Detection

    Relation Networks for Object Detection笔记  写在前面:关于这篇论文的背景知识,请参考我前面的两篇随笔(<关于目标检测>和<关于注意力机制> ...

  6. Learning to Compare: Relation Network for Few-Shot Learning 论文笔记

    主要原理: 和Siamese Neural Networks一样,将分类问题转换成两个输入的相似性问题. 和Siamese Neural Networks不同的是: Relation Network中 ...

  7. 【原】使用Bmob作为iOS后台开发心得——云端代码添加其他User的Relation关系

    本文转载请注明出处 —— polobymulberry-博客园 问题描述 我在User表中增加了两个列,分别为“我关注的人”(Relation关系)和“我的粉丝”(Relation关系)当我关注某个人 ...

  8. Laravel学习--关于Relation的坑

    前段时间比较忙,就没有坚持写博客,但发现这周末再想捡起来,好难,一直到了今天晚上,才决定坐下来写一篇,哈哈哈-- 最近在用 Laravel 5.2,踩了几个关于 Relation 的坑,在这里用博客记 ...

  9. c++ vector 使用

    1. 包含一个头文件: 1 #include <vector> 2. 申明及初始化: std::vector<int> first; // empty vector of in ...

随机推荐

  1. c# 发送web请求

    我们目前涉及到的现有的接收请求方式有三种, 第一种: 页面式的Form表单 第二种: 服务的webservice形式的xml 第三个: restful风格的post包体json 第一种比较老,博客园的 ...

  2. (翻译)Xamarin.Essentials 最新预览版的更多跨平台 API

    原文地址:https://blog.xamarin.com/cross-platform-apis-xamarin-essentials-latest-preview/ 在 Microsoft Bui ...

  3. js对象替换键值名称

    js对象替换键值名称 将obj中的id和name字段替换分别替换成为“@id”,“@name” 代码如下: let obj = [{id:,name:,name:"李四"}].ma ...

  4. PTA L2-002 链表去重 团体程序设计天梯赛-练习集

    L2-002 链表去重(25 分)   给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉.即对每个键值 K,只有第一个绝对值等于 K 的结点被保留.同时,所有被删除的结点须被保存在另 ...

  5. nigin配置安全:三个案例看Nginx配置安全(转)

    转:https://www.leavesongs.com/PENETRATION/nginx-insecure-configuration.html 三个案例看Nginx配置安全 PHITHON  之 ...

  6. javascript 的回调函数

    既然函数可以像其他数据那样赋值给某个个变量,可以被定义.删除.拷贝,那为什么就不能被当成参数传递给其他函数呢? 下面的示例中,我们定义了一个以两个函数为参数的函数.该函数会分别执行这两个参数函数,并返 ...

  7. 可持久化线段树(cf1080F)

    大佬博客 https://www.cnblogs.com/zinthos/p/3899565.html 题目:https://codeforces.com/problemset/problem/108 ...

  8. 洛谷——P1744 采购特价商品

    P1744 采购特价商品 题目背景 <爱与愁的故事第三弹·shopping>第一章. 题目描述 中山路店山店海,成了购物狂爱与愁大神的“不归之路”.中山路上有n(n<=100)家店, ...

  9. VB查询数据库之报表——机房收费系统总结(六)

    我们要用一个软件做报表的模板.然后在VB里面添加部件.代码调用模板,详细步骤如下. 一.下载安装 首先做报表要下载安装Grid++Report 4.5 报表设计器 点击下载(内含破解补丁) 二.制作模 ...

  10. 【Vijos 1607】【NOI 2009】植物大战僵尸

    https://vijos.org/p/1607 vijos界面好漂亮O(∩_∩)O~~ 对于一个植物x,和一个它保护的植物y,连一条边<x,y>表示x保护y,对于每个植物再向它左方的植物 ...