您的位置:首页 > 汽车 > 新车 > 哪个软件制作视频比较好_seo个人博客_黄冈网站推广_天津百度网络推广

哪个软件制作视频比较好_seo个人博客_黄冈网站推广_天津百度网络推广

2025/4/29 8:01:21 来源:https://blog.csdn.net/jmlangel_/article/details/146481189  浏览:    关键词:哪个软件制作视频比较好_seo个人博客_黄冈网站推广_天津百度网络推广
哪个软件制作视频比较好_seo个人博客_黄冈网站推广_天津百度网络推广

向量 v e c t o r vector vector)是 C C C++ 标准模板库( S T L STL STL)中封装的顺序容器 S e q u e n c e C o n t a i n e r Sequence\ Container Sequence Container),是一个能够存放任意类型动态数组,能够增加和压缩数据。

文章目录

  • 前言 —— 序列式容器(sequence containers)
    • 1. 容器的概观
    • 2. 容器的分类
    • 3. 序列式容器(sequence containers)
  • 一、vector 的介绍
  • 二、vector 的使用(常用接口)
    • 1. 常见构造
    • 2. iterator 的使用
    • 3. 容量操作
    • 4. 增删查改
  • 三、vector 迭代器失效问题(重点)
  • 四、vector 的深度剖析
    • 1. 内置类型构造函数
    • 2. 使用 memcpy 拷贝问题
    • 3. 动态二维数组理解
  • 五、vector 的模拟实现
  • 总结


前言 —— 序列式容器(sequence containers)

1. 容器的概观

一句话总结:容器,置物之所也。(存放数据 —— 数据结构)

研究数据的特定排序方式,以利于查找排序或其他特殊目的,这一专门学科我们称为数据结构 D a t a S t r u c t u r e s Data\ Structures Data Structures)。几乎可以说,任何特定的数据结构都是为了实现某种特定的算法。

S T L STL STL 容器即是将运用最广的一些数据结构实现出来:

a r r a y array array数组)、 l i s t list list链表)、 t r e e tree tree)、 s t a c k stack stack堆栈)、 q u e u e queue queue队列)、 h a s h t a b l e hashtable hashtable哈希表)、 s e t set set集合)、 m a p map map映射 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ······ ⋅⋅⋅⋅⋅⋅ 等等。

2. 容器的分类

根据 “ “ 数据在容器中的排列 ” ” 特性,将这些数据结构分为序列式 s e q u e n c e sequence sequence)和关联式 a s s o c i a t i v e associative associative)两种:

序列式容器( S e q u e n c e C o n t a i n e r s Sequence\ Containers Sequence Containers关联式容器( A s s o c i a t i v e C o n t a i n e r s Associative\ Containers Associative Containers
a r r a y array array C C C++ 11 11 11 R B − t r e e RB-tree RBtree非公开
v e c t o r vector vector s e t set set
h e a p heap heap以算法形式呈现 ( x x x _ h e a p ) (xxx\_heap) (xxx_heap) m a p map map
p e i o r i t y _ q u e u e peiority\_queue peiority_queue m u l t i s e t multiset multiset
l i s t list list m u l t i m a p multimap multimap
f o r w a r d _ l i s t forward\_list forward_list C C C++ 11 11 11 h a s h t a b l e hashtable hashtable C C C++ 11 11 11
d e q u e deque deque u n o r d e r e d _ s e t unordered\_set unordered_set C C C++ 11 11 11
s t a c k stack stack配接器 u n o r d e r e d _ m a p unordered\_map unordered_map C C C++ 11 11 11
q u e u e queue queue配接器 u n o r d e r e d _ m u l t i s e t unordered\_multiset unordered_multiset C C C++ 11 11 11
⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ······ ⋅⋅⋅⋅⋅⋅ u n o r d e r e d _ m u l t i m a p unordered\_multimap unordered_multimap C C C++ 11 11 11

各种容器之间存在内含( c o n t a i n m e n t containment containment)关系。例如:

  1. h e a p heap heap 内含一个 v e c t o r vector vector

  2. p r i o r i t y _ q u e u e priority\_queue priority_queue 内含一个 h e a p heap heap

  3. s t a c k stack stack q u e u e queue queue 都含一个 d e q u e deque deque

  4. s e t / m a p / m u l t i s e t / m u l t i m a p set/map/multiset/multimap set/map/multiset/multimap 都内含一个 R B − t r e e RB-tree RBtree

  5. u n o r d e r e d _ x unordered\_x unordered_x 都内含一个 h a s h t a b l e hashtable hashtable

3. 序列式容器(sequence containers)

所谓序列式容器,其中的元素都可序( o r d e r e d ordered ordered),但未必有序( s o r t e d sorted sorted)。

C C C++ S T L STL STL 中主要提供了以下序列式容器

  1. a r r a y array array定长数组

  2. v e c t o r vector vector变长数组

  3. l i s t list list双向链表

  4. f o r w a r d _ l i s t forward\_list forward_list单向链表

  5. d e q u e deque deque双端队列

  6. s t a c k stack stack

  7. q u e u e queue queue队列

  8. p r i o r i t y _ q u e u e priority\_queue priority_queue优先队列

其中, s t a c k stack stack q u e u e queue queue 由于只是将 d e q u e deque deque 改头换面而成,技术上被归类为一种配接器 a d a p t e r adapter adapter),即其改变了容器的接口,被称为容器配接器 c o n t a i n e r a d a p t e r container\ adapter container adapter)。

a r r a y array array f o r w a r d _ l i s t forward\_list forward_list 都是 C C C++ 11 11 11 才引入的新容器


一、vector 的介绍

v e c t o r vector vector 其实就是顺序表在实际应用中非常的重要且非常的好用。因此我们要熟悉其常用的接口,会使用,再到了解其底层,知其然且知其所以然。一些不常用的或者忘记的内容可以直接去查 C C C++ 官方文档 v e c t o r vector vector 的文档介绍

在这里插入图片描述


二、vector 的使用(常用接口)

下面只列举了 v e c t o r vector vector 最常用的几个接口,更多详细信息可以自行查官方文档: v e c t o r vector vector

可以看出 v e c t o r vector vector 真正采用模板:
t e m p l a t e < c l a s s T , c l a s s A l l o c = a l l o c a t o r < T > > c l a s s v e c t o r ; template<class\ T,\ class\ Alloc = allocator<T>>class\ vector; template<class T, class Alloc=allocator<T>>class vector;

1. 常见构造

构造 ( c o n s t r u c t o r ) (constructor) (constructor) 函数声明接口说明
v e c t o r ( ) vector() vector()重点无参构造
v e c t o r ( s i z e _ t y p e n , c o n s t v a l u e _ t y p e & v a l = v a l u e _ t y p e ( ) ) vector(size\_type\ n,const\ value\_type\&\ val = value\_type()) vector(size_type n,const value_type& val=value_type())构造并初始化 n n n v a l val val
v e c t o r ( c o n s t v e c t o r & x ) vector(const\ vector\&\ x) vector(const vector& x)重点拷贝构造
v e c t o r ( I n p u t I t e r a t o r f i r s t , I n p u t I t e r a t o r l a s t ) vector(InputIterator\ first, InputIterator\ last) vector(InputIterator first,InputIterator last)使用迭代器进行初始化构造
void test1()
{//1.无参构造vector<int> v1;//2.带参构造vector<int> v2(10);//3.构造+初始化(10个1)vector<int> v3(10, 1);//4.迭代器构造vector<int> v4(v2.begin() + 2, v2.end() - 3);//5.拷贝构造vector<int> v5(v3);cout << "v1:";for (auto i : v1) cout << i;cout << endl << "v2:";for (auto i : v2) cout << i;cout << endl << "v3:";for (auto i : v3) cout << i;cout << endl << "v4:";for (auto i : v4) cout << i;cout << endl << "v5:";for (auto i : v5) cout << i;cout << endl;
}

在这里插入图片描述

2. iterator 的使用

i t e r a t o r iterator iterator 的使用接口说明
b e g i n ( ) begin() begin() + + + e n d ( ) end() end()重点获取第一个数据位置的iterator/const_iterator;获取最后一个数据的下一个位置的 iterator/const_iterator
r b e g i n ( ) rbegin() rbegin() + + + r e n d ( ) rend() rend()获取最后一个数据位置的reverse_iterator/const_reverse_iterator;获取第一个数据前一个位置的reverse_iterator/const_reverse_iterator

在这里插入图片描述

void test2()
{vector<int> v;for (int i = 0; i < 10; i++) v.push_back(i);//正向迭代器vector<int>::iterator it = v.begin();cout << "iterator:        ";while (it != v.end()){cout << *it << " ";it++;}cout << endl;vector<int>::const_iterator cit = v.begin();cout << "const_iterator:  ";while (cit != v.end()){cout << *cit << " ";cit++;}cout << endl;//反向迭代器vector<int>::reverse_iterator rit = v.rbegin();cout << "reverse_iterator:";while (rit != v.rend()){cout << *rit << " ";rit++;}cout << endl;
}

在这里插入图片描述

3. 容量操作

容量空间接口说明
s i z e size size获取数据个数
c a p a c i t y capacity capacity获取容量大小
e m p t y empty empty判断是否为空
c l e a r clear clear清空 v e c t o r vector vector 的有效数据( c a p a c i t y capacity capacity 不变, s i z e size size 置为 0 0 0
r e s i z e resize resize重点改变 v e c t o r vector vector s i z e size size
r e s e r v e reserve reserve重点改变 v e c t o r vector vector c a p a c i t y capacity capacity
void print(const vector<int>& v)
{cout << "size:" << v.size() << endl;cout << "v:";for (int i = 0; i < v.size(); i++) cout << v[i] << " ";v.empty() == true ? cout << endl << "empty true" : cout << endl << "empty false";cout << endl << endl;
}void test3()
{vector<int> v;for (int i = 0; i < 10; i++) v.push_back(i);print(v);v.resize(5);	print(v);v.resize(8, 10);print(v);v.resize(12);	print(v);v.resize(0);	print(v);
}

在这里插入图片描述

void test4()
{vector<int> v;int size = v.capacity();cout << "making v grow:" << endl;for (int i = 0; i < 100; i++){v.push_back(i);if (size != v.capacity()){size = v.capacity();cout << "capacity changed:" << size << endl;}}
}

v s vs vs 下使用的 S T L STL STL 基本是按照 1.5 1.5 1.5 倍方式扩容:

在这里插入图片描述

直接使用 r e s e r v e reserve reserve 预留空间来避免 / / / 减少扩容:

void test5()
{vector<int> v;//使用reserve预留空间,减少扩容v.reserve(100);int size = v.capacity();cout << "making vector grow:" << endl;for (int i = 0; i < 100; i++){v.push_back(i);if (size != v.capacity()){size = v.capacity();cout << "capacity changed:" << size << endl;}}
}

在这里插入图片描述

r e s e r v e reserve reserve 既不会缩容,也不会改变大小:

void test6()
{vector<int> v(10, 1);cout << "size:    " << v.size() << endl;cout << "capacity:" << v.capacity() << endl << endl;//会增容且不改变大小v.reserve(20);cout << "size:    " << v.size() << endl;cout << "capacity:" << v.capacity() << endl << endl;//不会缩容且不改变大小v.reserve(5);cout << "size:    " << v.size() << endl;cout << "capacity:" << v.capacity() << endl << endl;
}

在这里插入图片描述

总结:

  1. c a p a c i t y capacity capacity 的代码在 v s vs vs g g g++ 下分别运行会发现: v s vs vs c a p a c i t y capacity capacity 是按 1.5 1.5 1.5 倍增长的, g g g++ 是按 2 2 2 倍增长的。

注意: v s vs vs P J PJ PJ 版本 S T L STL STL g g g++ 是 S G I SGI SGI 版本 S T L STL STL(具体增长多少是根据具体的需求定义的,思维不要固化)。

  1. r e s e r v e reserve reserve 只负责开辟空间,如果确定知道需要用多少空间, r e s e r v e reserve reserve 可以缓解 v e c t o r vector vector 增容的代价缺陷问题。

  2. r e s i z e resize resize开空间的同时还会进行初始化,影响 s i z e size size

4. 增删查改

v e c t o r vector vector 增删查改接口说明
p u s h _ b a c k push\_back push_back重点尾插
p o p _ b a c k pop\_back pop_back重点尾删
f i n d find find查找(注意这个是算法模块实现,不是 v e c t o r vector vector 的成员接口
i n s e r t insert insert p o s pos pos 位置之前,插入 v a l val val
e r a s e erase erase删除 p o s pos pos 位置的数据
s w a p swap swap交换两个 v e c t o r vector vector 的数据空间
o p e r a t o r [ ] operator[\ ] operator[ ]重点像数组一样访问

使用 p u s h _ b a c k ( ) push\_back() push_back() p o p _ b a c k ( ) pop\_back() pop_back() 插入删除:

void test7()
{vector<int> v;//尾插v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); i++) cout << v[i] << " ";cout << endl;//尾删v.pop_back();v.pop_back();for (int i = 0; i < v.size(); i++) cout << v[i] << " ";cout << endl;
}

使用 i n s e r t ( ) insert() insert() e r a s e ( ) erase() erase() 在指定位置插入删除(只能使用迭代器):

void test8()
{//C++11新语法:使用列表方式初始化vector<int> v{ 1,2,3,4,5 };for (auto i : v) cout << i << " "; cout << endl << endl;// 在指定位置前插入值为val的元素,比如:3之前插入30,如果没有则不插入// 注意:vector没有提供find方法,如果要查找只能使用STL提供的全局find// 1.先使用find查找3所在位置vector<int>::iterator pos = find(v.begin(), v.end(), 3);// 如果没有找到会返回 v.end()if (pos != v.end()){// 2.在pos位置之前插入10v.insert(pos, 10);					}for (auto i : v) cout << i << " "; cout << endl << endl;// 在0位置(头删)删除元素v.erase(v.begin());for (auto i : v) cout << i << " "; cout << endl << endl;//删除所有元素v.erase(v.begin(), v.end());v.empty() == true ? cout << "empty true" << endl : cout << "empty false" << endl;
}

在这里插入图片描述

注意: C C C++ 11 11 11 之后支持列表初始化:vector<int> v{ 1,2,3,4,5 };,非常方便。


三、vector 迭代器失效问题(重点)

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如: v e c t o r vector vector 的迭代器 i t e r a t o r iterator iterator 就是原生态指针 T ∗ T^* Ttypedef T value_type; typedef value_type* iterator;)。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。

对于 v e c t o r vector vector 可能会导致其迭代器失效的操作有:

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resizereserveinsertassignpush_back 等。

在这里插入图片描述

vector<int> v{ 1,2,3,4,5,6 };
for (auto i : v) cout << i << " ";
cout << endl;int x; cin >> x;
auto p = find(v.begin(), v.end(), x);
if (p != v.end())
{//insert以后pos就失效了,不要直接访问//v.insert(p, 10);//*p *= 10;//要访问就要更新这个失效迭代器的值p = v.insert(p, 10);	//insert要返回新pos*(p+1) *= 11;
}
for (auto i : v) cout << i << " ";
cout << endl;

在这里插入图片描述

因此,如 i n s e r t insert insert 操作在标准库中的实现是返回一个迭代器指向插入位置的元素,就是为了避免迭代器失效的问题,所以以后如果要访问 p o s pos pos 位置元素,要更新一下再访问pos = insert(pos,val);

在这里插入图片描述
在这里插入图片描述

  1. 指定位置元素的删除操作 —— —— —— erase
vector<int> v{ 1,2,3,4 };auto pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);cout << *pos << endl; // 此处会导致非法访问

e r a s e erase erase 删除 p o s pos pos 位置元素后, p o s pos pos 位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果 p o s pos pos 刚好是最后一个元素,删完之后 p o s pos pos 刚好是 e n d end end 的位置,而 e n d end end 位置是没有元素的,那么 p o s pos pos 就失效了。因此删除 v e c t o r vector vector 中任意位置上元素时, v s vs vs 就认为该位置迭代器失效了。

以下代码的功能是删除 v e c t o r vector vector 中所有的偶数,请问那个代码是正确的,为什么?

//要求删除所有的偶数
void test1()
{vector<int> v{ 1,2,3,4 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)v.erase(it);++it;}for(auto i : v) cout << i << " "; cout << endl;
}

t e s t 1 test\ 1 test 1 报错:

在这里插入图片描述
在这里插入图片描述

//要求删除所有的偶数
void test2()
{vector<int> v{ 1,2,3,4 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)it = v.erase(it);else++it;}for(auto i : v) cout << i << " "; cout << endl;
}

t e s t 2 test\ 2 test 2 正常运行:

在这里插入图片描述

因此, e r a s e erase erase 在标准库中的实现是返回一个迭代器指向删除位置的下一个元素,就是为了避免迭代器失效的问题,所以如果以后要访问 p o s pos pos 位置元素,要更新一下再访问pos = erase(pos);

在这里插入图片描述
在这里插入图片描述

注意: L i n u x Linux Linux 下, g g g++ 编译器对迭代器失效的检测并不是非常严格,处理也没有 v s vs vs 下极端。

// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{vector<int> v{1,2,3,4,5};for(size_t i = 0; i < v.size(); ++i)cout << v[i] << " ";cout << endl;auto it = v.begin();cout << "扩容之前,vector的容量为: " << v.capacity() << endl;// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效v.reserve(100);cout << "扩容之后,vector的容量为: " << v.capacity() << endl;// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会// 虽然可能运行,但是输出的结果是不对的while(it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}
程序输出:
1 2 3 4 5
扩容之前,vector的容量为: 5
扩容之后,vector的容量为: 100
0 2 3 4 5 409 1 2 3 4 5
// 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{vector<int> v{1,2,3,4,5};vector<int>::iterator it = find(v.begin(), v.end(), 3);v.erase(it);cout << *it << endl;while(it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}
程序可以正常运行,并打印:
4
4 5
// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{vector<int> v{1,2,3,4,5};// vector<int> v{1,2,3,4,5,6};auto it = v.begin();while(it != v.end()){if(*it % 2 == 0)v.erase(it);++it;}for(auto e : v)cout << e << " ";cout << endl;return 0;
}
========================================================
// 使用第一组数据时,程序可以运行
[sly@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11
[sly@VM-0-3-centos 20220114]$ ./a.out
1 3 5
=========================================================
// 使用第二组数据时,程序最终会崩溃
[sly@VM-0-3-centos 20220114]$ vim testVector.cpp
[sly@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11
[sly@VM-0-3-centos 20220114]$ ./a.out
Segmentation fault

从上述三个例子中可以看到: S G I S T L SGI\ STL SGI STL 中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果 i t it it 不在 b e g i n begin begin e n d end end 范围内,肯定会崩溃的。

v e c t o r vector vector 类似, s t r i n g string string插入 + + + 扩容操作 + + + e r a s e erase erase 之后,迭代器也会失效

string s("hello");
auto it = s.begin();
// 放开之后代码会崩溃,因为resize到20会string会进行扩容
// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
// 后序打印时,再访问it指向的空间程序就会崩溃
//s.resize(20, '!');
while (it != s.end())
{cout << *it;++it;
}
cout << endl;
it = s.begin();
while (it != s.end())
{it = s.erase(it);// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后// it位置的迭代器就失效了// s.erase(it);++it;
}

虽然 s t r i n g string string 也会有迭代器失效的问题,但是我们一般使用 s t r i n g string string 都是通过下标访问,不经常用迭代器。

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。


四、vector 的深度剖析

S T L STL STL源码剖析》 这本书中,根据 S G I S T L SGI\ STL SGI STL 源代码 <stl_vector.h> 分析出 v e c t o r vector vector实现如下:

在这里插入图片描述

v e c t o r vector vector 所采用的数据结构非常简单:线性连续空间

它以两个迭代器 s t a r t start start f i n i s h finish finish 分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器 e n d _ o f _ s t o r a g e end\_of\_storage end_of_storage 指向整块连续空间(含备用空间)的尾端:

template <class T, class Alloc = alloc>
class vector
{
public:typedef T 			value_type;typedef value_type*	iterator;
...
protected:iterator start;				//表示目前使用空间的头iterator finish;			//表示目前使用空间的尾iterator end_of_storage;	//表示目前可用空间的尾
...
};

运用 s t a r t start start f i n i s h finish finish e n d _ o f _ s t o r a g e end\_of\_storage end_of_storage 三个迭代器,便可轻松提供首尾标识begin()+end()大小size()容量capacity()空容器判断empty()注标运算子[] ··· 等机能:

template <class T, class Alloc = alloc>
class vector
{
...
public:typedef T 			value_type;typedef value_type*	iterator;typedef value_type& reference;typedef size_t 		size_type;iterator begin() { return start; }iterator end() { return finish; }size_type size() const { return end() - begin(); }size_type capacity() const { return end_of_storage - begin(); }bool empty() const { return begin() == end(); }reference operator [] (size_type n) { return *(begin() + n); }
...
};

在这里插入图片描述

1. 内置类型构造函数

对于模板类型变量缺省值的时候为了考虑类类型变量,因此缺省值会直接给默认构造函数(匿名对象),如 r e s i z e resize resize 函数:void resize (size_t n, T val = T());

在这里插入图片描述

此时,对于类类型可以有默认构造函数,那么 T T T 也有可能是内置类型,这个时候编译器会自动优化

/*内置类型*/int i(1);						//构造函数
int j = int();					//默认构造->匿名对象->拷贝构造
int k = int(2);					//构造函数->匿名对象->拷贝构造
cout << i << " " << j << " " << k << endl;char ic('a');					//构造函数
char jc = char();				//默认构造->匿名对象->拷贝构造
char kc = char('b');			//构造函数->匿名对象->拷贝构造
cout << ic << " " << jc << " " << kc << endl;double id(1.1);					//构造函数
double jd = double();			//默认构造->匿名对象->拷贝构造
double kd = double(2.2);		//构造函数->匿名对象->拷贝构造
cout << id << " " << jd << " " << kd << endl;/*自定义类型*/string is("hello");				//构造函数
string js = string();			//默认构造->匿名对象->拷贝构造
string ks = string("world");	//构造函数->匿名对象->拷贝构造
cout << is << " " << js << " " << ks << endl;

在这里插入图片描述

可见,为了适配模板类型函数给缺省值时要调用构造函数初始化,对于内置类型,编译器自动优化(相当于给内置类型一个构造函数)。

2. 使用 memcpy 拷贝问题

当我们自己实现一个 v e c t o r vector vector 的成员函数 r e s e r v e reserve reserve 时,会用到 m e m c p y memcpy memcpy 将原来需要扩容的空间直接拷贝重新动态开辟的一块空间 t m p tmp tmp

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* tmp = new T[n];memcpy(tmp, _start, sizeof(T) * old_size);delete[] _start;_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}
}

这时就造成了这么一个问题 —— m e m c p y memcpy memcpy浅拷贝,如果拷贝前的空间有指针指向其他的空间(需要深拷贝),如: s t r i n g string string 类型,在拷贝完后原来指针所指向的空间会直接释放掉,因此拷贝到的指针指向的空间就被释放掉了

namespace bite	//bite代表自己实现的vector的reserve版本(使用memcpy拷贝)
{int main(){vector<string> v;v.push_back("1111");v.push_back("2222");v.push_back("3333");return 0;}
}

问题分析:

  1. m e m c p y memcpy memcpy内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存
    空间中(浅拷贝)。

  2. 如果拷贝的是内置类型的元素, m e m c p y memcpy memcpy 既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时(需要深拷贝),就会出错,因为 m e m c p y memcpy memcpy 的拷贝实际是浅拷贝

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结论:如果对象中涉及到资源管理时,千万不能使用 m e m c p y memcpy memcpy 进行对象之间的拷贝,因为 m e m c p y memcpy memcpy 是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

3. 动态二维数组理解

vector<T>模板类型,因此 T T T 不仅可以是内置类型,也可以是自定义类型,如:stringvector ⋅ ⋅ ⋅ ··· ⋅⋅⋅ 等。当 T T T 这个类型为 vector<T> v 的时候,vector<vector<T>> vv 就代表了 v v vv vv 里面每一个元素存放的都是一个 vector<T> v(地址),即可以理解为二维数组

vector<vector<int>> vv(n) 构造一个 v v vv vv 动态二维数组, v v vv vv 中总共有 n n n 个元素,每个元素都是 v e c t o r vector vector 类型的,每行没有包含任何元素,如果 n n n 5 5 5 时如下所示:

在这里插入图片描述

v v vv vv元素填充完成之后,如下图所示:

在这里插入图片描述

二维数组的遍历( 3 3 3 种方式):

1. o p e r a t o r [ ] operator[\ ] operator[ ] 遍历:

vector<vector<int>> vv1(5, vector<int>(5, 1));	//匿名对象赋值
for (int i = 0; i < vv1.size(); i++)
{for (int j = 0; j < vv1[i].size(); j++){cout << vv1[i][j] << " ";//cout << vv1.operator[](i).operator[](j) << " ";}cout << endl;
}
cout << endl;

2. 迭代器遍历:

vector<vector<int>> vv2(5, vector<int>(5, 2));	//匿名对象赋值
for (auto it = vv2.begin(); it != vv2.end(); ++it)
{for (auto _it = it->begin(); _it != it->end(); ++_it){cout << *_it << " ";}cout << endl;
}
cout << endl;

3. 范围 f o r for for 遍历:

vector<vector<int>> vv3(5, vector<int>(5, 3));	//匿名对象赋值
for (auto& v : vv3)
{for (auto& i : v){cout << i << " ";}cout << endl;
}
cout << endl;

运行结果如下:

在这里插入图片描述


五、vector 的模拟实现

因为 v e c t o r vector vector 隶属于 C C C++ S T L STL STL 容器,即标准模板库。因此,由于模板不能分开写到两个文件里,所以之前对于类的声明和定义分离写到两个文件中是不可取的。从今往后,只要带模板的类,都只能写到一个文件中,但是声明和定义可以分离:声明写到类里面(类外要重新写模板参数),定义写到类外面,或者把所有东西都写到类里面(类内部默认为内联 i n l i n e inline inline)。

因此,总共分为两个文件: v e c t o r . h vector.h vector.h 用来存放 v e c t o r vector vector 的定义和实现; t e s t . c p p test.cpp test.cpp 用来测试。

注意:这里两个文件中的 v e c t o r vector vector 都是自己定义的,为了和 s t d : : v e c t o r std::vector std::vector 作区分,我们要单独创建一个命名空间,这里我用的是 n a m e s p a c e y b c namespace\ ybc namespace ybc

  1. v e c t o r . h vector.h vector.h
#pragma once
#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<assert.h>using namespace std;namespace ybc
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;/*vector(){}*/// C++11 前置生成默认构造vector() = default;//类模板的成员函数,还可以继续是函数模板template<class InputIterator>	//任意类型的迭代器初始化vector(InputIterator first, InputIterator last){while (first != last){this->push_back(*first);++first;}}vector(int n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}vector(const vector<T>& v){reserve(v.size());for (auto& i : v){push_back(i);//this->push_back(i);}}void clear(){_finish = _start;}/*//传统写法vector<T>& operator = (const vector<T>& v){if (this != &v){clear();reserve(v.size());for (auto& i : v){push_back(i);//this->push_back(i);}}return *this;}*/void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//现代写法vector<T>& operator = (vector<T> v){swap(v);	//拷贝构造传参直接交换(编译器甚至会优化)return *this;}~vector(){if (_start != nullptr){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}iterator begin(){return _start;}iterator end(){return _finish;}const iterator begin() const{return _start;}const iterator end() const{return _finish;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}bool empty() const{return _start == _finish;}void reserve(size_t n){if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, sizeof(T) * old_size); //浅拷贝for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];	//类类型会调用赋值重载(深拷贝)}delete[] _start;_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}}//主要是用来初始化void resize(size_t n, const T& val = T())	//调用默认构造构造一个匿名对象给缺省值(不确定T的类型){											//编译器会优化:内置类型也有构造函数的概念if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}}void push_back(const T& x){//扩容if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back(){assert(!empty());--_finish;}iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);//扩容if (_finish == _end_of_storage){//迭代器失效size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish;while (end > pos){*end = *(end - 1);--end;}*pos = x;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator it = pos;while (it != end() - 1){*it = *(it + 1);++it;}--_finish;return pos;}T& operator [] (size_t i){assert(i < size());return _start[i];}const T& operator [] (size_t i) const{assert(i < size());return _start[i];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};
}
  1. t e s t . c p p test.cpp test.cpp
#include"vector.h"namespace ybc
{template<class T>void print_vector(const vector<T>& v){//规定:没有实例化的类模板里面取东西,编译器不能区分这里的const_iterator是类型还是静态成员变量//typename vector<T>::const_iterator it = v.begin()//1.迭代器遍历for (auto it = v.begin(); it != v.end(); it++){cout << *it << " ";}cout << endl;//2.范围for遍历for (const auto& i : v){cout << i << " ";}cout << endl;}void test1(){vector<int> v;//下标遍历for (int i = 0; i < 10; ++i){v.push_back(i);cout << v[i] << " ";}cout << endl;print_vector(v);cout << endl;vector<double> vd;//下标遍历for (int i = 0; i < 10; ++i){vd.push_back(i * 1.1);cout << vd[i] << " ";}cout << endl;print_vector(vd);}void test2(){std::vector<int> v{ 1,2,3,4,5,6 };for (auto i : v) cout << i << " ";cout << endl;int x; cin >> x;auto p = find(v.begin(), v.end(), x);if (p != v.end()){//insert以后pos就失效了,不要直接访问//v.insert(p, 10);//*p *= 10;//要访问就要更新这个失效迭代器的值p = v.insert(p, 10);	//insert要返回新pos*(p + 1) *= 11;}for (auto i : v) cout << i << " ";cout << endl;}void test3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto i : v) cout << i << " "; cout << endl;//要求删除所有的偶数for (auto it = v.begin(); it != v.end();){if (*it % 2 == 0)it = v.erase(it);else++it;}for (auto i : v) cout << i << " "; cout << endl;}void test4(){/*内置类型*/int i(1);		//构造函数int j = int();	//默认构造->匿名对象->拷贝构造int k = int(2);	//构造函数->匿名对象->拷贝构造cout << "int:   " << i << " " << j << " " << k << endl;char ic('a');		//构造函数char jc = char();	//默认构造->匿名对象->拷贝构造char kc = char('b');//构造函数->匿名对象->拷贝构造cout << "char:  " << ic << " " << jc << " " << kc << endl;double id(1.1);			//构造函数double jd = double();	//默认构造->匿名对象->拷贝构造double kd = double(2.2);//构造函数->匿名对象->拷贝构造cout << "double:" << id << " " << jd << " " << kd << endl;/*自定义类型*/string is("hello");			//构造函数string js = string();		//默认构造->匿名对象->拷贝构造string ks = string("world");//构造函数->匿名对象->拷贝构造cout << "string:" << is << " " << js << " " << ks << endl;}void test5(){vector<int> v;v.resize(10, 2);v.reserve(20);cout << "size:    " << v.size() << endl;cout << "capacity:" << v.capacity() << endl;cout << endl;for (auto i : v) cout << i << " "; cout << endl;}template<class Containers>void print(const Containers& v){for (const auto& i : v){cout << i << " ";}cout << endl;}void test6(){//默认构造vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);cout << "v1:"; print(v1);//拷贝构造vector<int> v2 = v1;cout << "v2:"; print(v1);//赋值重载vector<int> v3;v3 = v1;cout << "v3:"; print(v1);//迭代器构造vector<int> v4(v1.begin(), v1.end());cout << "v4:"; print(v4);list<int> l{ 1,2,3,4 };vector<int> v5(l.begin(), l.end());cout << "v5:"; print(v5);//n个val初始化vector<string> v6(10, "hi");cout << "v6:"; print(v6);vector<int> v7(10, 1);	//会调用参数最匹配的构造函数:这里10和1默认为int类型cout << "v7:"; print(v7);//memcpy拷贝问题vector<string> v8;v8.push_back("111111");v8.push_back("222222");v8.push_back("333333");v8.push_back("444444");v8.push_back("555555");cout << "v8:"; print(v8);v8.push_back("666666");v8.push_back("777777");cout << "v8:"; print(v8);}
}int main()
{//ybc::test1();//ybc::test2();//ybc::test3();//ybc::test4();//ybc::test5();ybc::test6();return 0;
}

总结

v e c t o r vector vector S T L STL STL 封装好的一块能够动态开辟连续空间的模板类。随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。因此, v e c t o r vector vector 的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必因为害怕空间不足而一开始就要求使用定长数组,或者遭受自己动态开辟释放空间的麻烦,我们可以安心使用 v e c t o r vector vector用多少开辟多少

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com