c++ std::vector

  • 重点总结
  • Vector采用连续存储结构,每个元素在内存上是连续的,默认容量是0。
  • Vector内存空间不够时,会重新申请一块足够大的内存,把旧内存空间中的所有元素都拷贝进新内存空间中去,同时释放旧的空间。GCC是二倍扩容,VS13是1.5倍扩容。
  • Vector能非常好的支持随机存取,即使用 []操作符at() 访问其中元素,复杂度O(1)。
  • [插入删除]Vector 无需扩容时,插入insert() 和删除 earse() 复杂度是O(n),push_back(), pop_back() 复杂度为O(1).
  • [优缺点] 高效的随机访问。 对于删除或插入操作,执行效率不高,越靠后插入或删除执行效率越高。


概述

概述-1. vector底层原理

  • STL 众多容器中,vector 是最常用的容器之一,其底层所采用的数据结构非常简单,就只是一段连续的线性内存空间。
std::vector<int> vec = {1,2,3,4,5};
  • 对象vec存储在中,对象中的成员指针指向上的地址。
    在这里插入图片描述

  • 通过分析 vector 源码,vector 包 3 个迭代器(可以理解成指针)成员:

//_Alloc 表示内存分配器,此参数几乎不需要我们关心
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector{
    ...
protected:
    pointer _Myfirst;
    pointer _Mylast;
    pointer _Myend;		// 迭代器可以理解为指针
};
  • 其中,_Myfirst 指向的是中开辟内存处起始位置(第一个元素);_Mylast 指向当前最后一个元素的位置;_myend 指向整个容器内存空间的后一个地址。

概述-2. 基础功能

assign			修改容器中内容
at 				返回索引处元素的引用
back 			返回指向容器尾元素的迭代器
begin 			返回指向容器头的迭代器
capacity  		返回容器分配的空间大小
cbegin			返回指向容器头的迭代器-const
cend			返回指向容器尾元素后一个位置的迭代器 - const
clear 			清空容器
crbegin			返回指向容器最后一个元素的 逆序 迭代器 - const
crend			返回指向容器头元素前一个位置的 逆序 迭代器 - const
data			返回指向容器底层中内存数组的指针

emplace 		通过迭代器在指定位置插入新元素,move
emplace_back	在容器尾处插入元素新元素,move
empty			判断容器是否为空
end				返回指向容器尾的迭代器
erase 			删除元素
front			返回容器头元素的引用
get_allocator
insert			插入元素
max_size		返回容器可以容纳的最大元素数
operator=		重载运算符 =
operator[]		重载运算符 [],通过索引可返回对应元素的引用
pop_back 		删除容器的最后一个元素
push_back		在容器尾处插入元素,copy
rbegin			返回指向容器最后一个元素的 逆序 迭代器
rend			返回指向容器头元素前一个位置的 逆序 迭代器
reserve 		改变容器所分配空间的大小
resize 			改变容器的大小,如果new_size大于当前容量,则填充默认值
shrink_to_fit
size 			返回容器中元素个数
swap 			当前vector与作为参数的vector交换元素

1、构造函数、assign()

  • 头文件 #include<vector>
  • <1>. 构造函数,函数原型如下:
// 1. 创建一个空vector
explicit vector(const allocator_type& alloc = allocator_type());

// 2. 创建一个包含n个value(或元素默认值)的vector
explicit vector(size_type n);
         vector(size_type n, const value_type& val,
                 const allocator_type& alloc = allocator_type());
                 
// 3. 通过迭代器创建vector
template <class InputIterator>
  vector(InputIterator first, InputIterator last,
         const allocator_type& alloc = allocator_type());
         
// 4. 复制构造
vector(const vector& x);
vector(const vector& x, const allocator_type& alloc);

// 5. 移动构造
vector(vector&& x);
vector(vector&& x, const allocator_type& alloc);

// 6. 通过 initializer list 构造
vector (initializer_list<value_type> il,
		const allocator_type& alloc = allocator_type());

例: 与上述顺序相同

int main () {
    // constructors used in the same order as described above:
    std::vector<int> first;                               // [1]. empty vector of ints
    std::vector<int> second(4,100);                       // [2]. four ints with value 100
    std::vector<int> third(second.begin(),second.end());  // [3]. iterating through second
    std::vector<int> fourth(third);                       // [4]. a copy of third

    std::vector<int> fifth(std::move(third)); 			  // [5]. a move of third
	std::vector<int> sixth{1,2,3,4,5};					  // [6]. 
	
    std::cout << "The contents of fifth are:";
    for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}

打印结果:
The contents of fifth are: 100 100 100 100

  • <2>. assign(),使用新元素替容器中当前元素,并修改容器大小。函数原型如下:
template <class InputIterator>
  void assign(InputIterator first, InputIterator last);	// 1. 半开区间[first,last),新元素按区间内顺序构造
  
void assign(size_type n, const value_type& val);		// 2. 全部填充val

void assign(initializer_list<value_type> il);			// 3. initializer list

例: 下列注释与上述序号相同

int main () {
    std::vector<int> first;
    std::vector<int> second;
    std::vector<int> third;
    std::vector<int> third_;
    first.assign(7,100);                            // [2]. element: 7个100


    second.assign(first.begin(), first.end()-1);    // [1]. 通过迭代器assign, 5个元素

    int myints[] = {1776,7,4};
    third.assign(myints,myints+3);                  // [3]. 通过数组构造
    third_.assign( {1,2,3,4,5,999} );               // [3]. 或这样

    std::cout << "Size of first:  " << int (first.size())  << std::endl;
    std::cout << "Size of second: " << int (second.size()) << std::endl;
    std::cout << "Size of third:  " << int (third.size())  << std::endl;
    std::cout << "Size of third_: " << int (third_.size()) << std::endl;


    first.assign(3,1);
    std::cout << "====" << std::endl;               // **** 替换first 中之前的元素,size从7变成3
    std::cout << "Size of first:  " << int (first.size())  << std::endl;
    return 0;
}

Size of first: 7
Size of second: 6
Size of third: 3
Size of third_: 6
====
Size of first: 3


2、operator[]、at()区别, data() 功能介绍

  • <1>. operator[]at()
  • 相同点
      1. 可以使用 operator[] or at() 方法来访问容器中元素,均返回对应元素引用。
      2. 起始索引是0,最大有效索引是 vector.size()-1
  • 区别
      1. operator[] 不检查索引是否有效(原因是为了效率,总是强制下标越界检查会增加程序的性能开销。),遇到无效索引直接报错。
      2. at() 会检查索引是否有效,在遇到无效索引时会抛出 out_of_range 异常。

  • <2>. data(),返回指向容器底层中内存数组的指针,函数原型如下:
	  value_type* data() noexcept;
const value_type* data() const noexcept;

int main ()
{
    std::vector<int> myvector(5);       // int 默认值 0

    int* p = myvector.data();           // 指向容器底层数组 中第一个元素, p可以理解为数组名,数组名也是数组第一个元素的地址

    *p = 10;                            // [1]. 通过指针赋值
    ++p;                                // next element
    *p = 20;
    p[2] = 100;                         // [2]. 编译器将 p[index] 看做 *(p+index)

    std::cout << "myvector contains:";
    for (unsigned i=0; i<myvector.size(); ++i)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';				

    return 0;
}

打印结果:myvector contains: 10 20 0 100 0


3、size, max_size, capacity, resize, reserve 功能介绍

  • size(),返回容器当前元素个数。
  • max_size(),返回容器可以容纳的最大元素数。
  • capacity(),返回容器当前分配的空间大小,原型如下:
size_type size() const noexcept;
size_type max_size() const noexcept;
size_type capacity() const noexcept;
int main () {
    std::vector<int> myvector;

    // set some content in the vector:
    for (int i=0; i<100; i++)
        myvector.push_back(i);

    std::cout << "size: " 	  << myvector.size() 	 << "\n";
    std::cout << "capacity: " << myvector.capacity() << "\n";
    std::cout << "max_size: " << myvector.max_size() << "\n";
    return 0;
}

size: 100
capacity: 128
max_size: 1073741823

  • resize(),调整容器的大小,使其包含n个元素。
  • n小于当前容器的大小,则vec.size()减小至n,删除尾部超出范围的元素。
  • n大于当前容器的大小,则在容器末尾处插入一定数量的元素,使vec.size()n
  • 若参数包含 val,插入的新元素为val,否则,插入的新元素为类型默认值(如 int 0)。
void resize (size_type n);
void resize (size_type n, const value_type& val);
int main () {
    std::vector<int> myvector;

    // set some initial content:
    for (int i=1; i<10; i++)
        myvector.push_back(i); 	// 1,2,3,...,9

    myvector.resize(5);			// size=5,  element: 1,2,3,4,5  
    myvector.resize(8,100);		// size=8,			 1,2,3,4,5,100,100,100
    myvector.resize(12);		// size=12,			 1,2,3,4,5,100,100,100,0,0,0  int默认值为0

    std::cout << "myvector contains:";
    for (int i=0;i<myvector.size();i++)
        std::cout << ' ' << myvector[i];
    std::cout << '\n';

    return 0;
}

myvector contains: 1 2 3 4 5 100 100 100 0 0 0 0

  • reserve(n),调整容器 capacity 的大小。
  • n大于当前 vec.capacity(),则为容器重新分配存储空间,vec.capacity()将扩大至n或更大。
  • 其他情况下,不会重新分配存储空间, vec.capacity()值不受影响。
void reserve (size_type n);
int main () {
    std::vector<int>::size_type sz;

    std::vector<int> foo;
    sz = foo.capacity();

    std::cout << "making foo grow:\n";
    for (int i=0; i<100; ++i) {
        foo.push_back(i);
        if(sz!=foo.capacity()) {
            sz = foo.capacity();        // 容量不够, VS-1.5倍扩容, gcc-2倍扩容
            std::cout << "capacity changed: " << sz << '\n';
        }
    }

    std::vector<int> bar;
    sz = bar.capacity();
    bar.reserve(100);           		// 使用reserve() 修改容器capacity 大小

    std::cout << "making bar grow:\n";
    for (int i=0; i<100; ++i) {
        bar.push_back(i);
        if (sz!=bar.capacity()) {
            sz = bar.capacity();
            std::cout << "capacity changed: " << sz << '\n';
        }
    }
    return 0;
}

making foo grow:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128

making bar grow:
capacity changed: 100


4、insert() 功能介绍

  • 函数原型如下:
    cplusplus.com/reference/vector
      接下来按顺序对上述功能进行介绍,首先,创建一个包含字符元素的vector
using namespace std; 
int main(){
    //创建一个vector,置入字母表的前十个字符
    vector <char> vector_char;
    for( int i=0; i < 5; i++ )
        vector_char.push_back(i+65);
}

  • <1>. iterator insert (const_iterator position, const value_type& val);
    <函数功能>. 在指定位置position 前插入值为 val 的元素,返回指向这个元素的迭代器。
	// 续接前一节代码:
	// ... 打印为 ZABCDE
    vector_char.insert(vector_char.begin(),'Z');
    for (auto i:vector_char) cout << i;
}

  • <2>. iterator insert (const_iterator position, size_type n, const value_type& val);
    <函数功能>. 在指定位置position 前插入 n 个值为 val 的元素,返回一个迭代器。
	// 续接前一节代码:
    //... 打印结果为 WWZABCDE
    vector_char.insert(vector_char.begin(),2,'W');
    for (auto i:vector_char) cout << i;
}

  • <3>. iterator insert (const_iterator position, InputIterator first, InputIterator last);
    <函数功能>. 在指定位置position 前插入 另一个vector 的 Iterator.first至Iterator.last的元素,返回一个迭代器。
	// 续接前一节代码:
    // ... 打印 WWZABCDE WWZABCDE
    vector_char.insert(vector_char.end(),vector_char.begin(),vector_char.end());
    for (auto i:vector_char) cout << i;
}

5、遍历方法与排序

  • 三种方法遍历vector
  • [1]. 下标遍历
vector<int> arr{1,2,3,4,5};
for(int i=0; i<arr.size(); ++i)
    cout << arr[i] << " ";
    // or
    cout << arr.at(i) << " ";
  • [2]. 迭代器遍历
vector<int> arr{1,2,3,4,5};
for(vector<int>::iterator iter=arr.begin();it!=arr.end();it++)
    cout << *iter << " ";
  • [3]. C++11遍历
vector<int> arr{1,2,3,4,5};
for (auto& iter : arr)
	cout << iter << " ";
  • 正序排序, 顺序颠倒,逆序排序
// 使用头文件中算法 #include<algorithm>,
vector<int> arr{1,2,3,4,5,4,3,2,1,0};

// 正序排序
std::sort(arr.begin(),arr.end());

// 顺序颠倒
std::reverse(arr.begin(),arr.end());

// 逆序排序
std::sort(arr.rbegin(), arr.rend());

6、反向迭代器 rbegin(), rend()

  • rbegin(); rend() 返回逆序迭代器。crbegin(); crend() 返回const修饰的逆序迭代器。
std::vector<int> vec;
vec.rbegin() 	// 返回一个逆序迭代器,它指向容器vec的最后一个元素
vec.rend() 		// 返回一个逆序迭代器,它指向容器vec的第一个元素前面的位置

vec.crbegin() 	// 返回一个逆序迭代器-const,它指向容器vec的最后一个元素
vec.crend() 	// 返回一个逆序迭代器-const,它指向容器vec的第一个元素前面的位置
  • 如果容器是const的,则其返回类型要加上const_前缀,也就是const_iteratorconst_reverse_iterator类型。

在这里插入图片描述

  • 反向迭代器是一种反向遍历容器的迭代器。通过对 ++--运算符进行重载,使得:
  • 对于反向迭代器,++ 运算将访问前一个元素,-- 运算则访问下一个元素

    例1-逆序打印:

int main() {
    std::vector<int> vec{0,1,2,3,4,5,6,7,8,9};

    vector<int>::reverse_iterator r_iter;
    for (r_iter = vec.rbegin();         // 迭代器指向最后一个元素
         r_iter != vec.rend();          // rend() 指向第一个元素的前一个
         ++r_iter)					    // ++操作访问前一个元素
        std::cout << *r_iter << " ";    // prints 9,8,7,...0
    
    std::cout << "\n";
    return 0;
}

打印结果:9 8 7 6 5 4 3 2 1 0

  • 为了以降序排列容器中元素,需向 std::sort 传递一对反向迭代器:

    例2-降序排列:

int main() {
    std::vector<int> vec{1,2,3,0,5};

    // sorts vec in "normal" order, 0 1 2 3 5 
    std::sort(vec.begin(), vec.end());
    std::cout << "正序排列: ";
    for (auto& i:vec)
        std::cout << i << " ";
    
    // sorts in reverse: puts smallest element at the end of vec, 5 3 2 1 0
    std::sort(vec.rbegin(), vec.rend());
    std::cout << "\n逆序排列: ";
    for (auto& i:vec)
        std::cout << i << " ";

    return 0;
}

正序排列: 0 1 2 3 5
逆序排列: 5 3 2 1 0

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值