vector relation

::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)。

时间: 2024-08-28 17:19:27

vector relation的相关文章

NFA到DFA的转换

#include<iostream> #include<string> #include<cstring> #include<vector> #include<algorithm> #include<set> #define MAX 100 using namespace std; struct edge { char preNode; //节点表示只能用单个字符 char nextNode; char tchar; //转换字符 }

Generate a binary tree from parent-&gt;child relationship

This was asked in LinkedIn Interview Given a list of child->parent relationships, build a binary tree out of it. All the element Ids inside the tree are unique. Example: Given the following relationships: Child Parent IsLeft 15 20 true 19 80 true 17

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 Sep, 2014 QUESTION TOPICS Singular Value Decomposition Principal Component Analysis Intuitive Explanations Statistics (academic discipline) Machine Lear

学习STL -- 向量vector

在STL中向量vector是使用数组的形式实现的,因此向量具有顺序表的所有特点,可以快速随机存取任意元素.向量是同一种数据类型的对象的集合,每个对象根据其位置有一个整数索引值与其对应,类似于数组.与定义数组不同,向量在实例化是不需要声明长度,标准库负责管理和储存元素相关的内存,不用担心长度不够. vector容器中的元素是连续存放的,当容器中增加一个新元素的时候,如果原来的存储空间刚好被用完,那么系统需要重新申请一块更大的连续存储空间,把原来的元素复制到新的空间,并在最后添加新元素,最后再撤销久

Linear Classification: Support Vector Machine, Softmax

原文地址:http://cs231n.github.io/linear-classify/ ############################## 内容列表: 1.介绍线性分类器 2.线性成绩函数 3.解释一个线性分类器 4.损失函数 4.1.多类支持向量机 4.2 . Softmax分类器 4.3 . 支持向量机 vs Softmax 5.线性分类器的交互式web例子 6.总结 ###############################################3 Linear

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

初译 Support Vector Machines:A Simple Tutorial(一)

从本次开始我将开始尝试着逐章翻译一下 Alexey Nefedov的<Support Vector Machines:A Simple Tutorial>这本教材,这可是我们导师极力推荐的SVM教材,看了好久一直感觉一脸懵逼,索性开坑翻译一下吧,也当是加深理解,毕竟我也是一知半解,如果翻译的有不对的地方还望大佬们斧正,欢迎提意见,欢迎讨论. 嗯,就是这样. (一)Introduction 在本章节中将会介绍一些用于定义支持向量机(SVM)的基础的概念,这些概念对于理解SVM至关重要,假定读者了

Vector容器 和 iteration 迭代器

vector容器 vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值.和string对象一样,标准库负责管理存储元素的相关内存.我们把vector称为容器,是因为它可以包含其他对象.一个容器中的所有对象都必须是同一种类型的. 使用vector之前,必须包含相应的头文件.#include <vector> using std::vector; vector是一个类模板(class template).模板允许程序员编写单个类或函数定义,这个类和函数定义可用于不同的数据类型上.

C++ vector 使用

看vector 的使用: #include "stdafx.h" #include <iostream> #include <vector> using namespace std; int main() { vector<long> v; for (long i=0;i<10000;i++) { v.push_back(rand()); } cout << "size = " << v.size()