C++STL之vector容器:基本使用及模拟实现

目录

vector的概念

vector的使用

1.push_back / pop_back / insert / erase 的使用

push_back尾插

pop_back尾删

insert任意位置的插入

erase任意位置的删除

2.构造函数和拷贝构造

3.赋值运算符 operator=

4.遍历数据

5.迭代器

6.容量相关的接口

1. 在 VS 中容量的增长是按1.5倍增长的

2 .在Linux中,则是按 2 倍增长的

3. vector 插入数据是如何实现的,为什么增容增加 2倍,1.5倍不行吗?

迭代器失效(☆)

情景一:

情景二:

vector的模拟实现

源码的引入

基本结构的搭建

vector.h

vector的模拟实现

push_back 尾插

reserve 增容

pop_back 尾删

insert 任意位置的插入

erase 任意位置的删除

resize的实现

拷贝构造

operator= 赋值

memcpy拷贝问题

整体代码

vector.h

test.c

动态二维数组的理解


vector的概念

上一节,我们学习了string的使用及模拟实现。

String 是把单向信息存起来,比如身份证号码,而vector是把多项相同的信息存起来,比如每个人的姓名、年龄、分数 保存到 vector 容器中。

我们可以把 vector看成顺序表,只不过在顺序表的基础上增加了一些功能,提供了丰富的成员函数和操作符重载,如迭代器支持、各种算法的适配等。而顺序表比较单一。

string 和 vector<char> 有什么区别???

  • vector 是一个动态的数组,里面存指定的类型,不够都可以动态增长
  • string 也具有类似的动态特性,能够自动管理内存以适应字符串内容的变化
  • string 有 \0,vector 没有 \0
  • string 支持+=、输入操作
  • vector存其它类型+=,输入的意义就不大。

vector的使用

和string的使用类似,如果遇到不会的可以查阅文档。

1.push_back / pop_back / insert / erase 的使用

push_back尾插

#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto e : v) 
	{
		cout << e << " ";   // 1 2 3 4
	}
	cout << endl;
}

int main() 
{
	test_vector1();
	return 0;
}

pop_back尾删

#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//尾删
	v.pop_back();
	v.pop_back();
	for (auto e : v) 
	{
		cout << e << " ";   // 1 2 
	}
	cout << endl;
}

int main() 
{
	test_vector1();
	return 0;
}

insert任意位置的插入

insert 需要配合迭代器使用

#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.insert(v.begin(), 0); //头插 0
	v.insert(v.begin(), -1); //再头插 -1 
	v.insert(v.begin() + 1, 10);// 再插入完 -1 之后,再 -1的后面插入 10
	for (auto e : v)
	{
		cout << e << " ";  // -1  10  0  1  2  3  4
	}
	cout << endl;
}

int main() 
{
	test_vector1();
	return 0;
}

erase任意位置的删除

同样配合迭代器使用 v.erase( begin() + 1),  加几就是删掉下标为几的值

#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	//任意位置的删除
	v.erase(v.begin()); // 删除第一个位置,变成 2 3 4
	v.erase(v.begin() + 1); //2 3 4 中,删除 3 这个元元素
	for (auto e : v)
	{
		cout << e << " "; // 2  4
	}
	cout << endl;
}

int main() 
{
	test_vector1();
	return 0;
}

假设我要删除的数为指定的值的时候该怎么删除呢?

使用迭代器删除的时候相对于指定该数的下标,问题是我们不知道这个数的位置,如何删除呢?vector 中没有提供 find 查找功能

进入手册,在 C++ 的标准算法库<algorithm>中有 find 算法可以用于在容器中查找特定元素。(记得包文件)

#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
void test_vector1()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(5);
	v.push_back(30);
	v.push_back(40);

	vector<int>::iterator pos = find(v.begin(), v.end(), 5); //find用的算法里面的
	if (pos != v.end())
	{
		v.erase(pos);  // erase中依旧填 迭代器
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

}

int main() 
{
	test_vector1();
	return 0;
}

2.构造函数和拷贝构造

#include <iostream>
using namespace std;
#include <vector>
void test_vector2()
{
	vector<int> v1;   //构造,初始化 v1
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	vector<int> v2(v1); //拷贝构造 即初始化 v2
	for (size_t i = 0; i < v1.size(); ++i)
	{
		cout << v1[i] << " ";
	}
	cout << endl;
	for (size_t i = 0; i < v2.size(); ++i)
	{
		cout << v2[i] << " ";
	}
	cout << endl;
}

int main() 
{
	test_vector2();
	return 0;
}

3.赋值运算符 operator=

using namespace std;
#include <vector>
void test_vector3()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	vector<int> v3;
	v3.push_back(10);
	v3.push_back(20);
	v3.push_back(30);
	v3.push_back(40);
	v1 = v3;  //赋值
	for (size_t i = 0; i < v1.size(); ++i)
	{
		cout << v1[i] << " ";
	}	
	cout << endl;
}

int main() 
{
	test_vector3();
	return 0;
}

4.遍历数据

#include <iostream>
using namespace std;
#include <vector>
void test_vector4()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//1.遍历数据  修改数据
	//operator[] + size
	for (size_t i = 0; i < v.size(); ++i)
	{
		v[i] *= 2; //修改数据,都可以修改
		cout << v[i] << " ";
	}
	cout << endl;
	//2迭代器
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	//3.范围for  -> 被编译器替换成迭代器方式遍历支持的
	for (auto e : v)   // 加上引用后,修改会影响原容器中的元素
	{
		//e *= 2; //不加引用修改不会影响原来的容器
		cout << e << " ";
	}
	cout << endl;
}

int main() 
{
	test_vector4();
	return 0;
}

5.迭代器

迭代器有四种:非const 正向、非const 反向、const 正向、const 反向

const 对象通常在传参的时候才会产生,如果定义对象的时候就把对象定义成 const,那么都不能插入数据了

#include <iostream>
using namespace std;
#include <vector>
//const 正向迭代器,const不能写,可以读
void print_vector(const vector<int>& vt)
{
	vector<int>::const_iterator it = vt.begin();
	while (it != vt.end())
	{
		//*it = 1; 不能写
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
//const反向迭代器,
void print_rvector(const vector<int>& vt) 
{
	vector<int>::const_reverse_iterator rit = vt.rbegin();
	while (rit != vt.rend()) 
	{
		//不能写(修改)
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}
void test_vector5() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	//1.正向迭代器
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	
	//3.const 正向迭代器
	print_vector(v);
	//4.const 反向迭代器
	print_rvector(v);

	//2.反向迭代器
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}
int main() 
{
	test_vector5();
	return 0;
}

6.容量相关的接口

1. 在 VS 中容量的增长是按1.5倍增长的

#include <iostream>
using namespace std;
#include <vector>

void test_vector6() 
{
	
	vector<int> v;
	size_t sz = v.capacity();  // sz 记录初始容量
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);  //不断增加数据导致 capacity 发生改变,即增容
		if (sz != v.capacity())  //不相等表示还没增容不进去打印,更新的容量
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}
int main() 
{
	test_vector6();
	return 0;
}

2 .在Linux中,则是按 2 倍增长的

3. vector 插入数据是如何实现的,为什么增容增加 2倍,1.5倍不行吗?

1. 当空间足够的时候:进行插入操作(如push_back)时,首先会检查当前已分配的内存空间是否足够容纳新元素。如果空间足够,直接将新元素放置在合适的位置,并更新相关的指针和计数器(如_finish)。

当空间不足的时候:如果当前容量不足,就需要进行扩容操作。通常,vector会分配一块更大的连续内存空间,将原有元素复制到新的内存空间中,然后添加新元素,并更新相关的指针和计数器。

2.  以 2 倍的方式扩容,可以减少频繁扩容的次数。如果每次只增加较小的比例(如 1.5 倍),在数据量较大且频繁插入的情况下,可能会导致更多次的扩容操作,而每次扩容都涉及到内存分配、元素复制等开销较大的操作。以较大的比例扩容可以在一定程度上平衡内存使用和性能开销。

如果采用 1.5 倍的扩容策略,相对 2 倍扩容来说,在一定程度上可能会减少一些空间浪费。但是需要更频繁地进行扩容操作。因为每次扩容增加的空间相对较小,当数据持续插入时,可能会比 2 倍扩容更快地达到容量上限,从而导致更多次的内存分配和数据复制操作,这会带来一定的性能销。

综上所述:C++ 中 vector 常见的扩容策略有 2 倍和 1.5 倍。2 倍扩容减少频繁扩容次数,计算简单但可能浪费空间。1.5 倍扩容空间利用更紧凑,但可能导致更频繁扩容操作且计算复杂些。具体策略取决于实际应用场景对性能和内存的需求。

迭代器失效(☆)

情景一:

增容就导致迭代器失效了,我都已经开了新空间,迭代器还指向旧空间,所以迭代器失效了(无法使用该迭代器)。

push_back,insert,resize,reserve 等可能会扩容的都会导致迭代器失效。

#include <iostream>
using namespace std;
#include <vector>
void test_vector7() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	vector<int>::iterator it = v.begin();

	v.push_back(6);
	v.push_back(7);   //插入7就有问题了

	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

int main() 
{
	test_vector7();
	return 0;
}

如何解决:    ① 我们可以在插入完了之后,然后获取迭代器避免这种情况的发生。 

                    我们就是要以上代码的情况:我们需要再次获取迭代器  it = v.begin();

情景二:

在 VS 中,当执行了 erase 删除操作之后,迭代器就已经失效了,再执行 ++it,正好跳过了奇数 3,假设不是3,是30呢?也就是说,这里跳过了一次判断, 在VS中报错,是编译检查的。

Linux下编译不会报错,但是执行的结果可能侥幸通过,g++ vector.cpp –std=c++11

  • 段错误,删除6的时候有越界行为,我在加一行 push_back(7); 不会越界,可以执行出结果。
  • 连续的偶数的时候,下一个也不会删除。

#include <iostream>
using namespace std;
#include <vector>
void test_vector8()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	//要求删除容器中的所以偶数
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it); //传的就是迭代器
		}
		++it;
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}
int main() 
{
	test_vector8();
	return 0;
}

Linux中的结果:编译没有检查出错误,但是在执行的时候出现,段错误,是因为最后删除6的时候越界了。这只能说明Linux中没有进行严格的检查。

gcc 下面没有进行严格的检查,可能会报错,可能会正常运行,也可能有个别偶数没被删除掉。
总结: 不管哪个平台下,erase(it) 以后,it 就失效了,只是导致的结果不一样而己,总之都有各种各样的问题。

如何解决呢???

erase 有一个返回值,返回的是删除的 it 的下一个位置的迭代器

删除一个元素后,将这个新的迭代器赋值给 it,确保迭代器始终指向一个有效的元素,避免出现迭代器失效的情况。

vector<int>::iterator it = v.begin();
while (it != v.end())
{
	if (*it % 2 == 0)
	{
		it = v.erase(it); //传的就是迭代器
	}
	else 
	{
		++it;
	}
}

vector的模拟实现

源码的引入

以前我们定义结构的时候都是定义一个指针和_size、_capacity,而源码中使用的是三个指针,分别表示类似的意思: 指向动态数组,记录有效个数,记录容量,容量不够则需要增容。

基本结构的搭建

vector 的模拟实现和 string类 类似,都是在一个命名空间中定义。防止与标准库中的 vector 命名发生冲突。

vector.h
#pragma once
namespace my_vector 
{
	template<class T>
	class vector 
	{
	public:
		typedef T* iterator;    //假设T是int, iterator 就是 int*
		typedef const T* const_iterator;  //const 迭代器
		//迭代器
		iterator begin() 
		{
			return _start;
		}
		iterator end() 
		{
			return _finish;
		}
		//const迭代器
		const_iterator begin() const 
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
		//构造函数
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{}
		//析构函数
		~vector() 
		{
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}
		size_t size() const   //当对象传参给引用的时候,会加const,遍历数据调用size,const只能调用const
		{
			return _finish - _start;
		}
		size_t capacity() const //和size同理 
		{
			if (_start == nullptr && _finish == nullptr)  //第一次push数据的时候,两个空指针相减,不一定是0,保险起见,返回0;
				return 0;
			return _endofstorage - _start;
		}
		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;
		iterator _finish;
		iterator _endofstorage;
	};
}

vector的模拟实现

push_back 尾插

该逻辑和顺序表的尾插是一样的,空间不够增容,空间够了,直接尾插。

同时指针 _finish 向后移动一步

//尾插
void push_back(const T& x) 
{
	if (_finish == _endofstorage) 
	{
		size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
		reserve(newcapacity);   //接下来实现 reserve
	}
	*_finish = x;
	++_finish;
}

reserve 增容

开临时空间,拷贝数据到临时空间,释放旧空间,更新三个指针。

在这里如果这样写,会存在指针失效的问题,当我们开了新的空间,而 _size任然指向旧空间,size() = _size - _start ,_start在新空间,而_size在旧空间,一相减就会出现问题,不同空间的指针相减属于未定义的行为,计算capacity也是同理: _endofstorage - _start 也分别指向不同的空间。

//3,reserve增容
void reserve(size_t n)
{
	if (n > capacity()) 
	{
		T* tmp = new T[n];
		if (_start) 
		{
			memcpy(tmp, _start, sizeof(T) * size());
			delete[] _start;
		}
		_start = tmp;
		_finish = tmp + size();
		_endofstorage = tmp + capacity();
	}
}

如何解决

我们在开新空间之间就把 size计算好,这样就可以更新_finish 和 _endofstorage,这样就解决了指针失效的问题。

//3,reserve增容
void reserve(size_t n)
{
	if (n > capacity()) 
	{
		size_t sz = size();
		T* tmp = new T[n];
		if (_start) 
		{
			memcpy(tmp, _start, sizeof(T) * sz);
			delete[] _start;
		}
		_start = tmp;
		_finish = tmp + sz;
		_endofstorage = tmp + n;
	}
}

pop_back 尾删

//7,pop_back()
void pop_back() 
{
	assert(_start < _finish);   //保证可以有数据删除
	--_finish;
}

insert 任意位置的插入

同样会存在指针失效的问题。

当增容的时候 pos 失效了,没有扩容前 pos指向旧空间,扩容后pos指针还指向旧的空间,所以需要先计算好 pos 到_start 的距离。

void insert(iterator pos, const T& x) 
{
	assert(pos <= _finish);
	if (_finish == _endofstorage) 
	{
		size_t n = pos - _start;
		size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
		reserve(newcapacity);
		pos = _start + n;
	}
	iterator end = _finish - 1;
	while (end >= pos) 
	{
		*(end + 1) = *end;
		--end;
	}
	*pos = x;
	++_finish;
}

erase 任意位置的删除

iterator erase(iterator pos) 
{
	assert(pos < _finish);
	iterator it = pos;
	while (it < _finish) 
	{
		*it = *(it + 1); 
		++it;
	}
	--_finish;
	return pos;  //删除之后正好是下一个,直接返回pos
}

resize的实现

如果 T 是一个内置类型(如intdouble等),那么T()会产生一个相应类型的默认初始化值(对于整数通常是 0,对于浮点数通常是 0.0 等)。

string 类已经详解介绍了resize 的实现,逻辑都是一样的。

实现resize时候,为什么不能用memcpy 或 memset,按字节拷贝或设置,除了 0 给任何值都达不到这样的效果,因为mem系列的函数都是按字节处理。

0000 0000 0000 0000 0000 0000 0000 0000

如果是 1

0000 0001 0000 0001 0000 0001 0000 0001  = 很大的数字,而不是 1 这个值

所以 memset 只适合初始化为0

// resize(),不仅仅开空间,还初始化
void resize(size_t n, const T& val = T()) //给T类型的缺省值的意思
{
	if (n <= size()) 
	{
		_finish = _start + n;
	}
	else 
	{
		//增容
		if (n > capacity()) 
		{
			reserve(n);
		}
		while (_finish < _start + n) 
		{
			*_finish = val;
			++_finish;
		}
	
	}
}

拷贝构造

vector中也存在深浅拷贝的问题。

没有写拷贝构造就是浅拷贝,析构的时候就会析构两次。

拷贝构造也是构造的一种,开同样的大空间去初始化,_finsh 随着增加的值而变化。

方法一:

//拷贝构造 v2(v1)
vector(const vector<T>& v)  //const去调用,只能调用const,这就是为什么 capacity 和 size 加 const的原因
{
	_start = new T[v.capacity()];
	_finish = _start;
	_endofstorage = _start + v.capacity();
	for (size_t i = 0; i < v.size(); ++i) 
	{
		*_finish = v[i];  //因为v是const,所以调用 operator[]() const
		++_finish;
	}
}

方法二:

// 拷贝构造 方法二
vector(const vector<T>& v) 
	:_start(nullptr)
	,_finish(nullptr)
	,_endofstorage(nullptr)
{
	reserve(v.capacity());  //先开好空间
	for (const auto& e : v) 
	{
		push_back(e);  //push的过程中 _finish,和_endofstorage随之变化
	}
}

operator= 赋值

传统写法:

//赋值 operator=
vector<T>& operator=(const vector<T>& v) 
{
	if (this != &v)
	{
		delete[] _start;
		_start = new T[v.capacity()];
		size_t sz = v.size();
		/*for (size_t i = 0; i < sz; ++i)
		{
			_start[i] = v[i];
		}*/
		memcpy(_start, v._start, sizeof(T) * sz); //少使用mem系列,因为是按字节处理的
		_finish = _start + sz;
		_endofstorage = _start + v.capacity();
		
	}
	return *this;
}

现代写法:

//赋值现代写法===>建议写这个
vector<T>& operator=(vector<T> v)   //先调用构造函数初始化 v
{
	swap(v);   //this->swap(v);
	return *this;
}
void swap(vector<T>& v)
{
	std::swap(_start, v._start);  //我没展开std,如果展开了就写成 ::swap表示调用的是全局的swap
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}


memcpy拷贝问题

假设我们在 vector 中存储的是字符串

vector<std::string> v;

 v.push_back("1111111111111111111111111111111");
 v.push_back("2222222222222222222222222222");
 v.push_back("3333333333333333333333333333");
 v.push_back("4444444444444444444444444444");

  for (auto e : v)

  {

      std::cout << e << " ";

  }

我们在插入string 类型的时候,字符串存在于堆上,当vector容量不够的时候,需要增容,而在 reserve 中,我们增容的时候把旧空间的数据拷贝过去,我们使用的是memcpy拷贝,memcpy是按字节拷贝,也就是浅拷贝,_start 也会指向那块空间,当我们指向 delete[] _start的时候,空间被释放了,新空间中的_start 还指向被释放的空间,一个指向被释放空间的指针就是野指针,当程序结束后还要执行析构,还会释放该空间,所以不能使用memcpy去拷贝。 正常情况下,我的delete没错,是memcpy导致我们两指针都指向了同一块空间。

void reserve(size_t n)
	{
		if (n > capacity()) 
		{
			size_t sz = size();
			T* tmp = new T[n];
			if (_start) 
			{
				memcpy(tmp, _start, sizeof(T) * sz);//按字节拷贝,浅拷贝
				delete[] _start;
			}
			_start = tmp;
			_finish = tmp + sz;
			_endofstorage = tmp + n;
		}
	}

如何解决? 不能使用 memcpy 进行拷贝

void reserve(size_t n)
{
	if (n > capacity()) 
	{
		size_t sz = size();
		T* tmp = new T[n];
		if (_start) 
		{
			//memcpy(tmp, _start, sizeof(T) * sz);//按字节拷贝,浅拷贝
			for (size_t i = 0; i < sz; ++i) 
			{
				tmp[i] = _start[i]; // 调用的是T的 operator=深拷贝
			}
			delete[] _start;
		}
		_start = tmp;
		_finish = tmp + sz;
		_endofstorage = tmp + n;
	}
}

整体代码

vector.h

#pragma once
namespace my_vector
{
	template<class T>
	class vector 
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{}
		拷贝构造 v2(v1)
		//vector(const vector<T>& v)  //const去调用,只能调用const
		//{
		//	_start = new T[v.capacity()];
		//	_finish = _start;
		//	_endofstorage = _start + v.capacity();
		//	for (size_t i = 0; i < v.size(); ++i) 
		//	{
		//		*_finish = v[i];  //调用[] const
		//		++_finish;
		//	}
		//}
	
		// 拷贝构造 方法二
		vector(const vector<T>& v) 
			:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)
		{
			reserve(v.capacity());  //先开好空间
			for (const auto& e : v) 
			{
				push_back(e);
			}
		}

		赋值 operator=
		//vector<T>& operator=(const vector<T>& v) 
		//{
		//	if (this != &v)
		//	{
		//		delete[] _start;
		//		_start = new T[v.capacity()];
		//		size_t copySize = v.size();
		//		/*for (size_t i = 0; i < copySize; ++i)
		//		{
		//			_start[i] = v[i];
		//		}*/
		//		memcpy(_start, v._start, sizeof(T) * v.size());
		//		_finish = _start + copySize;
		//		_endofstorage = _start + v.capacity();
		//		
		//	}
		//	return *this;
		//}
		//赋值现代写法===>建议写这个
		vector<T>& operator=(vector<T> v) 
		{
			swap(v);
			return *this;
		}
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}
		//析构函数
		~vector() 
		{
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}
		//6,const_iterator
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
		//4,迭代器
		iterator begin() 
		{
			return _start;
		}
		iterator end() 
		{
			return _finish;
		}
		//3,reserve增容
		void reserve(size_t n)
		{
			if (n > capacity()) 
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start) 
				{
					//memcpy(tmp, _start, sizeof(T) * sz);//按字节拷贝,浅拷贝
					for (size_t i = 0; i < sz; ++i) 
					{
						tmp[i] = _start[i]; // 调用的是T的 operator=深拷贝
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = tmp + sz;
				_endofstorage = tmp + n;
			}
		}
		//10,resize(),不仅仅开空间,还初始化
		void resize(size_t n, const T& val = T()) //给个T类型的缺省值的意思
		{
			if (n <= size()) 
			{
				_finish = _start + n;
			}
			else 
			{
				//增容
				if (n > capacity()) 
				{
					reserve(n);
				}
				while (_finish < _start + n) 
				{
					*_finish = val;
					++_finish;
				}
			
			}
		}
		//1,尾插
		void push_back(const T& x) 
		{
			/*if (_finish == _endofstorage) 
			{
				size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			++_finish;*/
			insert(_finish, x);
		}
		//7,pop_back()
		void pop_back() 
		{
			/*assert(_start < _finish);
			--_finish;*/
			erase(_finish - 1); //干掉最后一个位置
		}
		//8,insert()
		void insert(iterator pos, const T& x) 
		{
			assert(pos <= _finish);
			if (_finish == _endofstorage) 
			{
				size_t n = pos - _start;
				size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
				reserve(newcapacity);
				pos = _start + n;
			}
			iterator end = _finish - 1;
			while (end >= pos) 
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		//9,erase
		iterator erase(iterator pos) 
		{
			assert(pos < _finish);
			iterator it = pos;
			while (it < _finish) 
			{
				*it = *(it + 1); 
				++it;
			}
			--_finish;
			return pos;  //删除之后正好是下一个
		}
		//5,operator[]
		T& operator[](size_t i) 
		{
			assert(i < size());
			return _start[i];  //*(_start + i)
		}
		const T& operator[](size_t i) const
		{
			assert(i < size());
			return _start[i];
		}
		//2,计算size()、capacity()
		size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			if (_start == nullptr && _endofstorage == nullptr)
				return 0;
			return _endofstorage - _start;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
	void print_vector(const vector<int>& v) //传参引用接受、就会加const
	{
		vector<int>::const_iterator it = v.begin();
		while (it != v.end()) 
		{
			//*it += 1;
			std::cout << *it << " ";
			++it;
		}
		std::cout << std::endl;
	}
	void test_vector1() 
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		
		print_vector(v);

		vector<int>::const_iterator it = v.begin();
		while (it != v.end()) 
		{
			//*it += 1;
			std::cout << *it << " ";
			++it;
		}
		std::cout << std::endl;
		for (auto& e : v) 
		{
			//e -= 1; 要修改就用引用 auto&
			std::cout << e << " ";
		}
		std::cout << std::endl;
		for (size_t i = 0; i < v.size(); ++i) 
		{
			std::cout << v[i] << " ";
		}
		std::cout << std::endl;
	}
	void test_vector2() 
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		v.push_back(6);
		v.insert(v.begin(), 0);
		print_vector(v);
		
		vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else 
			{
				++it;
			}
		}
		print_vector(v);
	}
	void test_vector3() 
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);

		v.push_back(3);
		v.push_back(4);
		v.resize(10);

		print_vector(v);
		std::cout << v.size() << std::endl;
		std::cout << v.capacity() << std::endl;

		v.resize(4);

		print_vector(v);
		std::cout << v.size() << std::endl;
		std::cout << v.capacity() << std::endl;

		v.push_back(5);
		v.resize(12,12);

		print_vector(v);
		std::cout << v.size() << std::endl;
		std::cout << v.capacity() << std::endl;
	}
	void test_vector4()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

	/*	vector<int> v2(v1);
		for (size_t i = 0; i < v1.size(); ++i)
		{
			std::cout << v1[i] << " ";
		}
		std::cout << std::endl;

		for (size_t i = 0; i < v2.size(); ++i)
		{
			std::cout << v2[i] << " ";
		}
		std::cout << std::endl;*/
		
		//v1 = v3
		vector<int> v3;
		v3.push_back(10);
		v3.push_back(20);
		v3.push_back(30);
		v3.push_back(40);
		v1 = v3;
		for (auto e : v1) 
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
	}
	void test_vector5() 
	{
		vector<std::string> v;
		v.push_back("1111111111111111111111111111");
		v.push_back("2222222222222222222222222222");
		v.push_back("3333333333333333333333333333");
		v.push_back("4444444444444444444444444444");
		for (auto e : v) 
		{
			std::cout << e << " ";
		}
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
//using namespace std;
#include <string>
#include "vector.h"
int main() 
{
	//my_vector::test_vector1();
	//my_vector::test_vector2();
	//my_vector::test_vector3();
	//my_vector::test_vector4();
	my_vector::test_vector5();
	return 0;
}

动态二维数组的理解

其实这就是C语言中的二维数组,只不过这里换成了vector,可以动态增长。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值