C++——vector

一、基本概念

vector就是顺序表模板。使用时需要包含头文件vector.h

vector不支持流插入和流提取(数据的多样性)。

二、构造函数

(1)无参构造

不传数据,size为0。

(2)有参构造

用n个val构造。

意思就是无参的默认构造的plus版,无参的默认构造其实就是实例化了模板,并没有开空间。这是在无参默认构造的基础上开了n个空间。

(3)拷贝构造

用Bvector对象构造Avector对象。

cpp 复制代码
int main() {
	vector<int> n1;//无参构造(默认为0个数据)
	vector<int> n2(6,1);//有参构造(用6个1构造n2)
	vector<int> n3(n2);//拷贝构造(用n2构造n1)
	cout << n1.size() << endl;//输出0

	for (int i = 0; i < n2.size(); i++) {//有参构造
		cout << n2[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < n3.size(); i++) {//拷贝构造
		cout << n3[i] << " ";
	}
	cout << endl;
}

(4)迭代器构造

可以用数组、另一个对象的迭代器构造新对象。

cpp 复制代码
int main() {
	int arr[5] = { 1,2,3,4,5 };
	vector<int> n1(arr,arr+4);//数组构造
		for (int i = 0; i < n1.size(); i++) {
		cout << n1[i] << " ";
	}
	cout << endl;
	vector<int> n2(5,0);
	vector<int> n3(n2.begin(),n2.end()-1);//n2对象的迭代器
	for (int i = 0; i < n3.size(); i++) {
		cout << n3[i] << " ";
	}
	cout << endl;
}

三、遍历

可以用[]、迭代器、范围for遍历vector对象。

cpp 复制代码
int main() {
	vector<const char*> n(5, "love");
	for (int i = 0; i < n.size(); i++) {//[]遍历
		cout << n[i] << " ";
	}
	cout << endl;

	vector<const char*>:: iterator it = n.begin();//迭代器遍历
	while (it != n.end()) {
		cout << *it << " ";
		it++;
	}
	cout << endl;

	for (auto cur : n) {//范围for遍历
		cout << cur << " ";
	}
	cout << endl;
}

四、reserve

size表示有效元素的个数;capacity表示当前空间的大小;resreve表示开空间,一般编译器不会进行缩容,就算缩容也不会缩到size以下。

cpp 复制代码
int main() {
	vector<int> n(10,1);
	cout << n.size() << endl << n.capacity() << endl;
	cout << endl;
	n.reserve(100);
	cout << n.size() << endl << n.capacity() << endl;
}

五、push_back/pop_puck

push_back(尾插);pop_back(尾删);insert(在某个位置插入数据,要使用迭代器)

cpp 复制代码
int main() {
	vector<int> n(1,2);
	n.push_back(0);//2后尾插一个0
	for (auto n1 : n) {
		cout << n1 << " ";
	}
	cout << endl;
	n.pop_back();//删除一个数据(0)
	for (auto n1 : n) {
		cout << n1 << " ";
	}
	cout << endl;
}
cpp 复制代码
int main() {
	vector<int> v(6, 0);
	v.insert(v.begin() + 2, 3);//在第三个位置插入3
		for (auto n : v) {
		cout << n << " ";
	}
	cout << endl;
}

六、erase/clear/swap

(1)erase

删除某个位置的数据;删除从first到last位置的数据(使用迭代器)

cpp 复制代码
int main() {
	int arr[] = { 0,1,2,3,4 };
	vector<int> v(arr,arr+5);
	v.erase(v.begin());//头删除(删除0)
	for (auto n : v) {
		cout << n << " ";
	}
	cout << endl;
	v.erase(v.begin(),v.begin()+3);//删除前3个数据
	for (auto n : v) {
		cout << n << " ";
	}
	cout << endl;
} 

(2)clear

清理对象属性不处理空间。

cpp 复制代码
int main() {
	vector<int> v(100, 0);
	cout << v.capacity() << " " << v.size() << endl;
	v.clear();
	cout << v.capacity() << " " << v.size() << endl;//size变为0,capacity不变
}

(3)swap

交换两个对象的属性,不交换空间。

cpp 复制代码
int main() {
	vector<int> v1(3, 0);
	vector<int> v2(3, 1);
	v1.swap(v2);//交换后v1变为3个1,v2变为3个0
	for (auto n : v1) {
		cout << n << " ";
	}
	cout << endl;
	for (auto n : v2) {
		cout << n << " ";
	}
	cout << endl;
} 

七、resize

调整元素个数。第二个参数缺省值为0。
resize需要扩容时capacity(n>size)会发生变化;不需要扩容时(n<=size)capacity可能变化也可能不变化(由编译器决定)

cpp 复制代码
int main() {
	vector<int> v(6,2);
	v.resize(2);//size调整为2
	cout << v.size() <<" "<<  v.capacity() << endl;
	for (auto n : v) {
	cout << n << " ";
	}
	cout << endl << endl;
	v.resize(4,1);//size调整为4,不足的用1补
	cout << v.size() << " " << v.capacity() << endl;
	for (auto n : v) {
		cout << n << " ";
	}
}

八、二维数组

我们来看这样一段代码

cpp 复制代码
int main() {
	vector<int> v(3, 1);//实例化第一个模板(一维数组)
	vector<vector<int>> vv(3, v);//实例化第二个模板(二维数组)
	for (int i = 0; i < vv.size(); i++) {
		for (int j = 0; j < v.size(); j++) {
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
	vv[2][1] = 2;//修改元素
	for (int i = 0; i < vv.size(); i++) {
		for (int j = 0; j < v.size(); j++) {
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
}

vector<vector<int>>实质就是动态顺序表每一层又套了一个动态顺序表。实例化了两个vector模板,一个是vector<int>,一个是vector<vector<int>>。这里并不等价于于二维数组,只是物理结构上类似。你写成vector<strintg>/vector<int*>也是模拟成了二维数组。如下图:

九、OJ题

题目链接:118. 杨辉三角 - 力扣(LeetCode)

思路:构建一个动态的二维数组(每一行的元素个数不同),将每一个元素的值初始化为1。接着从第三行开始遍历每一行的非首非尾元素并修改数据。(第一行和第二行以及每一行的首尾元素不需要修改)

代码实现:

十、vector的模拟实现

(1)注意

在实现string时,我们进行定义和声明的分离实现。因为string是类。在这里,模拟实现vector必须把定义和声明写在同一个头文件里,因为vector是模板类,不支持定义和声明分离的这种写法。
const数据只能传给const引用/指针,非const数据可以传给const。(const可以接受任意的)
迭代器是左闭右开的[begin(),end())。

(2)迭代器失效

①insert的迭代器失效

在实现insert扩容时出现迭代器失效的问题。迭代器失效就是指原有的空间已经释放了,你还在原有的空间上进行操作。(使用了insert之后如果发生扩容就不要访问之前的pos了)

ptrdiff_t:C/C++都能使用。有符号整型,表示两个指针相减的类型,大小和指针相同。

(3)resize的设计

参数为什么这么设计

size_type表示的就是无符号的指针相减类型,通常是size_t

value_type就是T。

value_type()表示T如果实例化的是内置类型,就初始化为0,如果是自定义类型就调用对应的默认构造。

可以形象的理解为内置类型的默认构造()。

(4)模板类里写模板函数

在vector模板类里写迭代器构造就是解决迭代器不是原生指针的问题。

(5)模拟实现

cpp 复制代码
//模拟实现顺序:命名空间->模板类框架->private->size、capacity、empty->reserve->[]
//push_back->pop_back->insert->resize->析构->拷贝构造->clear->重载=->迭代器构造->n个val构造->拷贝构造函数的深浅拷贝问题
#pragma once
#include<cstring>
#include<iostream>
#include<assert.h>
namespace my_code {
	template<class T>
	class vector {
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		void reserve(size_t n){//申请空间
			if (n > capacity()) {
				size_t old_size = size();
				iterator tmp = new T[n];
				memcpy(tmp,_start,old_size*sizeof(T));
				delete[] _start;
				_start = tmp;
				_finish = tmp + old_size;
				_end_of_storage = tmp + n;
			}
		}
		size_t size()const {//返回元素个数(const写了更严谨,承诺我不修改数据)
			return _finish - _start;
		}
		size_t capacity()const {//返回可存储元素最大个数(const写了更严谨,承诺我不修改数据)
			return _end_of_storage - _start;
		}
		bool empty() {//判空
			return _start == _end_of_storage;
		}
		iterator begin() {//普通迭代器
			return _start;
		}
		iterator end() {//普通迭代器
			return _finish;
		}
		const_iterator begin()const {//const迭代器
			return _start;
		}
		const_iterator end()const {//const迭代器
			return _finish;
		}
		void push_back(const T& x) {//尾插
			if (_finish == _end_of_storage) {
				reserve(size() == 0 ? 4 : 2 * size());
			}
			*_finish = x;
			_finish++;
		}
		void vector_print() {//打印
			iterator it = begin();
			while (it != end()) {
				std::cout << *it << " ";
				it++;
			}
			std::cout << std::endl;
		}
		T& operator[](size_t i){//重载[],[]就是用来查找和修改数据的,所以返回值是T&
			assert(i < _finish);
			return _start[i];
		}
		const T& operator[](size_t i) const{//重载const[]
			assert(i < _finish);
			return _start[i];
		}
		void pop_back() {
			assert(!empty());
			_finish--;
		}
		void insert(iterator pos,const T& x) {//在指定迭代器的位置插入数据
		//传引用是为了减少拷贝,用const修饰是因为常量的类型为const(const数据只能传const;const可以接受任意的)
			assert(pos >=_start && pos < _finish);
			if (_finish == _end_of_storage) {//扩容
				ptrdiff_t len = pos - _start;//解决迭代器失效的问题
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;//扩容后,原空间已经被释放,pos会发生改变
			}
			iterator end = _finish - 1;//挪动数据
			//*(end + 1) = *end;第一个挪动
			//*pos - 1 = *pos;最后一次挪动
			while (end >= pos) {
				*(end + 1) = *end;
				end--;
			}
			*pos = x;//插入数据
			_finish++;
		}
		void resize(size_t n,T val = T()) {//resize
			if (n < size()) {
				_finish = _start + n;
			}
			else {
				reserve(n);
				while (_finish < _start + n) {
					*_finish = val;
					++_finish;
				}
			}
		}
		~vector() {//析构
			if (_start) {
				delete[] _start;
				_start = _finish = _end_of_storage;
			}
		}
		//vector(){}//默认构造
		vector() = default;//强制生成默认构造,这里写默认构造的原因就是如果你写了任意的构造,
		//那么编译器就不会生成其它的默认构造,比如说你写了拷贝构造,那么编译器就不会生成无参的默认构造
		vector(const vector<T>& v) {
			reserve(v.size());
			for (auto& e : v) {
				push_back(e);
			}
		}
		void clear() {//clear
			_finish = _start;
		}
		//vector<T>& operator=(const vector<T>& v) {//重载=的传统写法
		//	if (this != &v) {//第一步是先清理空间,防止自己给自己赋值
		//		clear();
		//		reserve(v.size());
		//		for (auto& e : v) {
		//			push_back(e);
		//		}
		//		return *this;
		//	}
		//}
		void swap(vector<T>& v) {//swap
			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;
		}
		template <class InputIterator>
		vector(InputIterator first,InputIterator last) {
			while (first != last) {
				push_back(*first);
				++first;
			}
		}
		vector(int n,const T& val = T()) {
			reserve(n);
			for (size_t i = 0; i < n; i++) {
				push_back(val);
			}
		}
	private:
		iterator _start = nullptr;//这里初始化为nullptr目的就是减少构造函数的代码量
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}

相关推荐
wicb91wJ61 小时前
手写一个Promise,彻底掌握异步原理
开发语言·前端·javascript
黎阳之光1 小时前
黎阳之光:以视频孪生硬核实力,抢抓交通科技新机遇
大数据·人工智能·算法·安全·数字孪生
WL_Aurora1 小时前
2026天梯赛题解
python·算法
Engineer邓祥浩2 小时前
知识点1 时间复杂度、空间复杂度
java·数据结构·算法
小小仙。2 小时前
IT自学第三十七天补充
java·开发语言
少司府2 小时前
C++基础入门:初识模板
开发语言·c++·c·模板·函数模板·类模板·泛型编程
jinanwuhuaguo2 小时前
OpenClaw范式深度剖析:从技术突破到安全治理的系统性研究(第二篇)
开发语言·人工智能·安全·架构·kotlin·openclaw
lly2024062 小时前
C++ 命名空间
开发语言
问水っ2 小时前
Qt高级编程 第7章 用QtConcurrent实现线程处理
java·开发语言