当前位置: 首页 > news >正文

「C++」vector的使用及接口模拟详解

目录

前言

vector的使用

模拟实现

成员变量

reserve扩容

insert插入

erase删除

迭代器失效总结

resize

构造

默认构造和拷贝构造

迭代器区间构造

n个参数val的构造

赋值重载

模拟实现时的其他问题

typename语法

内置类型的构造函数

类模板中的类名简写

后记

附:完整代码


前言

大家好呀~欢迎来到海盗猫的CPP专栏——vector篇

本篇我们将以string的学习基础为对比,讲解vector的不同点和模拟实现中遇见的各种问题

vector是一个类模板,必需要显示实例化vector<typename>

vector的使用,与string相同的接口使用方法类似,但也有不同,可以查询文档

vector文档:

vector - C++ Reference

vector的使用

1.reserve规定不会缩小容量

在string中,reserve操作当n小于size时,不同的编译器会有不同的处理方法,vs2022中,reserve只会进行扩容;g++4.8则会缩小容量到有效字符的长度;

这是因为string规定中,没有明确写明这种情况的处理方式,所以不同的编译器可以有不同的处理方法;

而在vector的文档中查询可以看到

当n大于capacity容量时,才会扩容,其他情况均不会产生影响

2.resize会删除数据

当参数n小于当前size时会执行删除数据的操作,将第n个数据后的数据删除;

n大于capacity则会扩容到n

3.vector没有实现流插入和流提取的重载

这是由于vector能存储各种不同的类型,包括自定义类型,所以输出方式不尽相同,需要我们自行输出

4.insert,erase函数只有使用迭代器调用的重载(存在迭代器失效问题,后文模拟实现提及)


模拟实现

由于从vector容器开始,将使用类模板来进行其模拟实现,且成员变量替换为三个迭代器类型,所以模拟实现会出现许多不同的地方和注意点,据情况做出详细的解释;

成员变量

在string的模拟实现中三个成员变量为

char* _str = nullptr;//指向数据存储的空间地址
size_t _size = 0;//有效字符串长度
size_t _capacity = 0;//容量大小,不包括'\0'

而到了vector,三个成员变量将使用iterator的迭代器类型来声明:

public:typedef T* iterator;
private:iterator _start = nullptr;//有效空间开始位置的指针iterator _finish = nullptr;//有效空间结束位置的指针iterator _end_of_storage = nullptr;//容量最后位置的指针

reserve扩容

注意使用old_size临时变量来辅助更新迭代器

	void reserve(size_t n){if (n > capacity()) {size_t old_size = size();T* tmp = new T[n];memmove(tmp, _start, old_size * sizeof(T));//第三参数为移动的空间字节数delete[] _start;_start = tmp;//_finish = _start + size();//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,//将会导致size()结果出错,进而导致此处_finish的更新出错//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish_finish = _start + old_size;_end_of_storage = _start + n;}	}

此时还有一个比较难以发现的缺陷,实例:

当我们使用string来测试扩容

可以看到代码运行错误了,内置类型并不会出现这种错误

这是因为:

string中我们知道,成员变量中有指针类型;而此时,我们reserve中使用的使memmove来复制数据,而memmove的拷贝是以字节为单位的浅拷贝

浅拷贝将导致拷贝后的指针仍然指向原本的位置,随后delete[] _start;​将指向的空间释放,致使tmp变为野指针,进而_start也为野指针;且我们的测试代码放在一个函数中,在结束函数之前,对象会自动调用析构函数释放空间,这将导致_start指向的空间被析构两次,导致出现错误

所以拷贝数据的方法需要改变:

	//II.使用for循环调用[]来进行数据的拷贝,此时即便为自定义类型,也会调用对应的[]和赋值重载,由此实现深拷贝void reserve(size_t n) {if (n > capacity()) {size_t old_size = size();T* tmp = new T[n];for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;//_finish = _start + size();//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,将会导致size()结果出错,进而导致此处_finish的更新出错//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish_finish = _start + old_size;_end_of_storage = _start + n;}}

此时即便为自定义类型,也会调用对应的[]和赋值重载,由此实现深拷贝,来让tmp指向构造出的新空间。

insert插入

对于vector容器,insert插入时,使用迭代器进行插入;

  • 由于insert插入牵扯到扩容机制,若调用insert时产生了扩容,调用对象的成员变量都已经指向新空间,而pos仍然指向旧空间;

迭代器失效的版本:

	//I.//由于扩容机制的存在,扩容后成员变量全体都指向了新空间,而此处参数pos还指向旧空间void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}
  • 此处为第一种迭代器失效:此时pos相当于一个野指针

解决办法为:

记录pos的相对位置,扩容完成后,再使用新_start来更新其指向

解决迭代器失效:

	//II.//修正pos迭代器,防止迭代器失效void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}

此时测试代码:

void test_vector1() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;//查找数字x,在其位置上插入一个数字,并将原本该位置的数字*=10int x;cin >> x;//vector<int>::iteratorauto p = std::find(v.begin(), v.end(), x);if (p != v.end()) {//insert返回插入数据的pos位置//p意义已经改变(迭代器失效)//原本p为指向x的迭代器,插入后,p已经指向了插入的数据的位置,此时p不能直接访问v.insert(p, 40);(*p) *= 10;//p = v.insert(p, 40);//(*(p + 1)) *= 10;}print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;
}

输入x=2,此时结果为:

可见,我们原意为,在x=2的位置插入新数字,并将2*=10,但结果错误,原因为:

insert时产生了扩容,虽然在内部我们更新了形参pos的指向,但形参不影响实参,所以此时外部访问实参p,p仍然指向旧空间,此时迭代器失效,所以访问不到数字2的位置,导致修改失败;

  • 所以insert函数还需要将形参更新后的指向,传回给实参,因此给insert加上返回值,返回插入数据空间更新后的迭代器(地址);

返回形参pos更新后的内容:

//III.
//使insert返回pos形参的新指向,用于更新参数pos在该函数体外部的实参的指向,防止外部的实参还指向旧空间,致使迭代器失效
//ps:不能通过将形参pos设置为引用来解决pos实参的更新问题,因为使用&,即(iterator& pos),将导致参数为v.begin()+1这一类时出错,因为此时参数为一个具有常性的临时变量;
//这个问题也不能通过给引用加const,即(const iterator& pos)来解决,因为这样pos在insert函数内就不能修改了
iterator insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

此时将将测试代码插入语句修改为p = v.insert(p, 40);(*(p + 1)) *= 10;​再测试:

即可获得正确的结果

  • 此处也引出了第二种迭代器失效:

即,insert以后,即便insert返回更新了实参p,但其实际意义也已经改变,所以我们使用了(*(p + 1)) *= 10;​来修改x=2的值,使其*=10​;

而在我们自己实现的insert中,若没有产生扩容,此时p即便没有通过返回值更新,也可以通过直接(*(p + 1)) *= 10;​来对x=2修改(因为没有异地扩容,还是指向原来的位置),此时并不会报错,但实际上p的含义也已经改变(原本实参p是用来指向x=2这个数据的);

但在std::vector中,不论insert有没有扩容,迭代器实际有没有指向错误,编译器都会默认直接报错,以防止访问失效的迭代器,此时就必须更新迭代器才能继续访问;

erase删除

由于vector类模板可以接受许多不同类型以及自定义类型的数据,所以erase也只能使用迭代器来删除数据,且不再像string中提供len参数一样来删除指定长度

vector中erase默认删除单个元素,或者删除某个迭代器区间(左闭右开)的元素

由于erase删除也涉及了数据的挪动,所以erase和insert一样,执行操作后,都默认将导致迭代器失效(VS直接报错)

即和insert同理,即便在我们模拟实现的erase中,不存在内存空间的修改时,直接访问更新前的迭代器,理论上可以做到同样的修改操作,但在VS的std::vector中,由于严格检查,即便迭代器本身没有错误,也会认为其迭代器失效,必须要更新才可以访问使用。

  • 单元素删除erase模拟代码
	iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish) {*(it - 1) = *it;++it;}--_finish;//返回删除位置的迭代器,即被删除元素在删除前的下一个位置的迭代器return pos;}

erase返回值为被删除元素或区间,在被删除前的指向下一个位置元素的迭代器

  • 但由于erase会把删除位置的后续元素往前挪动,所以实际上pos位置的迭代器直接就是指向目标位置的
void test_vector2() {vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//删除所有的偶数print_container(v);auto it = v.begin();while (it != v.end()){if ((*it % 2) == 0) {it = v.erase(it);//模拟实现没有严格的检查,即便此处it没有更新,也不会报错//但若改为std::vector,此处若没有it来接收返回更新后的迭代器,就会直接报错}else {//因为erase会返回删除元素被删除前的下一位元素的迭代器,所以it只有在没有调用erase时才++it++;}}print_container(v);
}
  • 区间删除erase
	iterator erase(iterator begin, iterator end) {assert(begin >= _start, begin < _finish);assert(end >= _start, end < _finish);size_t len = end - begin;iterator it = end;while (it < _finish) {*(it - len) = *it;++it;}_finish -= len;return begin;}

迭代器失效总结

VS环境下严格检测,出现迭代器失效都会默认报错;而在;linux的g++环境下,没有出现实际错误的迭代器就不会报错

  1. 相当于野指针

    对内存进行修改之后,都认为迭代器失效(reserve中有讲)

  2. 迭代器意义改变

    insert后,由于对象内部的内容改变,所以insert之后默认参数迭代器的意义已经改变,所以使用时都不要直接访

    问,VS下访问会直接报错(不管这个迭代器参数实际上有没有)

    erase同理,删除数据后,其 实参迭代器的意义也会改变,也会默认报错

  3. string中也有迭代器失效,只是在我们之前的使用中,大多insert和erase都直接使用通过下标来操作的重载,而很少使用迭代器的重载版本

resize

由于vector的模拟实现使用类模板,且不能确定vector存储数据的类型,所以此处参数val的缺省值不能给类似数字0,或者字符'\0'一类的数据,而是使用模板类型T类型的默认构造函数,通过构造函数来构造一个匿名对象用于缺省值。

	void resize(size_t n, T val = T()) {if (n < size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;++_finish;}}}

但原本内置类型是不存在构造的,将导致如果T为此时为内置类型,将无法兼容这里的用法

由此,cpp中引入了内置类型的构造和析构函数的概念,内置类型也可以像自定义类型一样,使用默认构造来初始化变量int a(0);int b(1);​

构造

前文中,我们没有书写构造函数,代码也可以正常运行,这是因为编译器在类没有构造函数时,会自动生成一个无参构造函数,且成员变量不论是否在构造函数中显示构造,都会自动进行初始化列表,所以前文中的三个迭代器都使用了默认值nullptr来进行了构造

默认构造和拷贝构造

	vector() = default;//强制生成默认构造//直接使用范围for与尾插来拷贝数据vector<T>(const vector<T>& v) {reserve(v.size());for (auto i : v) {push_back(i);}}

迭代器区间构造

使用迭代器区间来构造

	template <class InputIterator>vector(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);++first;}}

此处使用函数模板来实现,这是因为,当传入的迭代器区间不是vector,但数据类型与调用构造的对象相同时,也使其可以构造出该对象

list<int> i(5, 2);
vector<int> v1(i.begin(),i.end());//使用list的迭代器区间来构造vector

若不使用模板,而是直接使用vector内的iterator类型就会导致出错


n个参数val的构造

使用n个val值来构造

	//n个val构造vector(size_t n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}

测试代码:

当只传参数n时,代码正常运行

此时出现了意外情况:

当我们传入参数val值时,却出现了错误,且报错信息显示错误点是在迭代器区间的构造函数中:

这说明此处函数调用就出现了错误,其原因为:

1.)我们知道,编译器会自动匹配与传参类型最相近的函数来调用;

2.)而上述情况中,由于编译器对于整型默认判断为int类型,由于我们的参数n为size_t类型,这里就会产生隐式类型转换,且val为int导致两个参数类型不同

3.)而对于迭代器区间的构造函数模板,此处两个参数传值都为int类型其类型相同,模板函数中俩个参数类型也相同,可直接推导为int,所以对于编译器来说,迭代器区间的构造函数显然更符合所传参数的类型;

解决方法很简单,将参数n类型换位int即可,因为此时对于这种特殊情况来说,就有了符合两个参数都为int的函数,这样也不会去使用迭代器区间构造的函数模板了(当有符合条件的函数存在时,不会使用函数模板来推导出一个新函数)

	vector(int n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}

赋值重载

赋值重载与string中一样,使用自定义的swap函数,来直接交换底层的迭代器指向;而赋值重载的参数则使用传值传参,过程中直接就会调用拷贝构造来进行深拷贝出一个新对象

	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;}

模拟实现时的其他问题

typename语法

详见笔记 模板初阶:函数模板 模块

template <class T>
void print_vector(const vector<T>& v) {//规定,没有实例化的类模板里取东西,编译器区分其实际意义//此处const_iterator,编译器就不能自动分别其为类型,还是一个静态成员变量//所以手动使用typename来告诉编译器,其为类型;或者使用auto直接使用v.begin()推导其类型//vector<T>::const_iterator it = v.begin();typename vector<T>::const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()) {cout << *it << ' ';++it;}
}

内置类型的构造函数

为了兼容自定义类型在传参时使用默认构造来实现缺省值,cpp中将内置类型也加入了构造概念,可以像类一样,用使用构造的方法来定义内置类型

	int a(0);int b(1);int c = int();//匿名对象double d = double();

类模板中的类名简写

在类模板中,使用类型名称时,可以直接用类名来替代如vector<T>​在类模板内部,就可以直接使用vector​来替代

例如:

		vector(const vector& v) {reserve(v.size());for (auto i : v) {push_back(i);}}vector<T>(const vector<T>& v) {reserve(v.size());for (auto i : v) {push_back(i);}}

后记

本期对于vector的学习就到这里了,文中只对常用的接口进行了解释和模拟,有错误的地方感谢大家指出,我们下期再见~

本期专栏:C++_海盗猫鸥的博客-CSDN博客

个人主页:海盗猫鸥-CSDN博客

附:完整代码

vector.h

#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
namespace hdmo {template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector<T>() = default;//强制生成默认构造vector(const vector& v) {reserve(v.size());for (auto i : v) {push_back(i);}}//vector<T>(const vector<T>& v) {//	reserve(v.size());//	for (auto i : v) {//		push_back(i);//	}//}//迭代器区间构造//vector(iterator first, iterator last) {//	while (first != last) {//		push_back(*first);//		++first;//	}//}template <class InputIterator>vector<T>(InputIterator first, InputIterator last) {while (first != last) {push_back(*first);++first;}}//n个val构造//vector(size_t n, const T& val = T()) {//	resize(n);//	//reserve(n);//	for (size_t i = 0; i < n; i++)//	{//		push_back(val);//	}//}vector<T>(int n, const T& val = T()) {reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}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<T>() {if (_start) {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;}//错误示范,[]返回应为对应位置的对象,而不是指针//iterator operator[](size_t pos) {//	assert(pos < size());//	return _start + pos;//}T& operator[](size_t pos) {assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const {assert(pos < size());return _start[pos];}//I.memmove移动数据,存在缺陷//这是因为,memmove函数是以字节为单位拷贝数据的,但对于自定义类型,存在指针时。就会出现浅拷贝的情况,导致memmove拷贝的指针和原指针指向同一个地址,导致delete[]释放空间后,出现野指针//void reserve(size_t n) {//	if (n > capacity()) {//		size_t old_size = size();//		T* tmp = new T[n];//		memmove(tmp, _start, old_size * sizeof(T));//第三参数为移动的空间字节数//		delete[] _start;//		_start = tmp;//		//_finish = _start + size();//		//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,将会导致size()结果出错,进而导致此处_finish的更新出错//		//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish//		_finish = _start + old_size;//		_end_of_storage = _start + n;//	}//}//II.使用for循环调用[]来进行数据的拷贝,此时即便为自定义类型,也会调用对应的[]和赋值重载,由此实现深拷贝void reserve(size_t n) {if (n > capacity()) {size_t old_size = size();T* tmp = new T[n];for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;//_finish = _start + size();//若这时调用size(),由于_start已经更新为新地址,而此时的_finish还指向原来的就空间,将会导致size()结果出错,进而导致此处_finish的更新出错//因此使用变量old_size,保存原本的size长度,再与已经更新的_start相加来得到更新后的_finish_finish = _start + old_size;_end_of_storage = _start + n;}}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void clear(){_finish = _start;}bool empty() const{return (size() == 0);}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;}/*//I.//由于扩容机制的存在,扩容后成员变量全体都指向了新空间,而此处参数pos还指向旧空间void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}//II.//修正pos迭代器,防止迭代器失效void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;}*///III.//使insert返回pos形参的新指向,用于更新参数pos在该函数体外部的实参的指向,防止外部的实参还指向旧空间,致使迭代器失效//ps:不能通过将形参pos设置为引用来解决pos实参的更新问题,因为使用&,即(iterator& pos),将导致参数为v.begin()+1这一类时出错,因为此时参数为一个具有常性的临时变量;//这个问题也不能通过给引用加const,即(const iterator& pos)来解决,因为这样pos在insert函数内就不能修改了iterator insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);//等于_finish时为尾插//扩容if (_finish == _end_of_storage) {size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//reserve扩容之后,由于存储空间的转变,pos迭代器失效,需要重置pos指向pos = _start + len;}//挪动数据iterator end = _finish - 1;//若pos迭代器没有更新,将导致迭代器失效//原因是:pos还指向原来旧空间的位置,而end是新空间_finish - 1的来的迭代器,大小关系无法确定while (end >= pos) {*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish) {*(it - 1) = *it;++it;}--_finish;return pos;}iterator erase(iterator begin, iterator end) {assert(begin >= _start, begin < _finish);assert(end >= _start, end < _finish);size_t len = end - begin;iterator it = end;while (it < _finish) {*(it - len) = *it;++it;}_finish -= len;return begin;}void resize(size_t n, T val = T()) {if (n < size()) {_finish = _start + n;}else {reserve(n);while (_finish < _start + n) {*_finish = val;++_finish;}}}private:iterator _start = nullptr;//有效空间开始位置的指针iterator _finish = nullptr;//有效空间结束位置的指针iterator _end_of_storage = nullptr;//容量最后位置的指针};
}

测试代码:

#include"vector.h"
#include<list>
#include<string>
using std::list;
using std::string;
using namespace hdmo;
using std::cout; 
using std::cin;
using std::endl;
template <class T>
void print_vector(const vector<T>& v) {//规定,没有实例化的类模板里取东西,编译器区分其实际意义//此处const_iterator,编译器就不能自动分别其为类型,还是一个静态成员变量//所以手动使用typename来告诉编译器,其为类型;或者使用auto直接使用v.begin()推导其类型//vector<T>::const_iterator it = v.begin();typename vector<T>::const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()) {cout << *it << ' ';++it;}//for (size_t i = 0; i < v.size(); i++)//{//	cout << v[i] << ' ';//}//for (auto i : v) {//	cout << i << ' ';//}cout << endl;
}template <class Container>
void print_container(const Container& v) {auto it = v.begin();while (it != v.end()) {cout << *it << ' ';++it;}cout << endl;
}void test_vector1() {std::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);//v.push_back(4);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;//cout << v.empty();//v.pop_back();//vector<int>::iterator it1 = v.insert(v.begin(), 8);//vector<int>::iterator it2 = v.insert(v.end(), 0);//cout << it1 - v.begin() << endl;//cout << it2 - v.begin() << endl;//print_vector(v);//查找数字x,在其位置上插入一个数字,并将原本该位置的数字*=10int x;cin >> x;//vector<int>::iteratorauto p = std::find(v.begin(), v.end(), x);if (p != v.end()) {//insert返回插入数据的pos位置//p意义已经改变(迭代器失效)//原本p为指向x的迭代器,插入后,p已经指向了插入的数据的位置,此时p意义改变// v.insert(p, 40);//(*p) *= 10;v.insert(p, 40);(*(p + 1)) *= 10;}print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;
}void test_vector2() {//std::vector<int> v;vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//删除所有的偶数//print_container(v);//auto it = v.begin();//while (it != v.end())//{//	if ((*it % 2) == 0) {//		it = v.erase(it);//	}//	else {//		it++;//	}//}//print_container(v);//cout << v.size() << endl;//cout << v.capacity() << endl;//v.erase(v.begin(), v.end());////v.erase(v.end() - 1, v.end());//print_container(v);//cout << v.size() << endl;//cout << v.capacity() << endl;v.resize(5, 0);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(10, 3);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(25, 7);v.reserve(20);print_container(v);cout << v.size() << endl;cout << v.capacity() << endl;
}
void test_vector3() {//int a(0);//int b(1);//int c = int();//匿名对象//cout << a << b << c << endl;////double d = double();vector<int> v1;v1.resize(5, 1);vector<int> v2(v1);vector<int> v3;v3 = v2;print_container(v2);print_container(v3);
}
void test_vector4() {//list<int> i(5, 2);//vector<int> v2;//v2.push_back(1);//v2.push_back(2);//v2.push_back(3);//v2.push_back(4);//vector<int> v1(i.begin(),i.end());//vector<int> v1(v2.begin(),v2.end() - 1);//print_container(v1);//vector<int> v3(10);//print_container(v3);vector<char> v4(10,'x');print_container(v4);v4.push_back('y');print_container(v4);//vector<string> v5(10,"xxx");//print_container(v5);//v5.push_back("yyy");//print_container(v5);
}
int main() {test_vector4();return 0;
}

http://www.dtcms.com/a/594640.html

相关文章:

  • 企业网站建设案例有哪些公司西峡微网站开发
  • 国外设计网站大全附近做广告招牌的
  • NLP入门——文本表示概述
  • HYPE分布式水文模型建模方法:基本输入文件制备、驱动数据制备、HYPE模型运行与手动调参、自动率参等
  • FreeBSD14.3中ZFS文件系统与samba设置仅指定用户可编辑的共享
  • 超酷个人网站商务网站建设考试题库
  • C++之内联变量(Inline Variables)
  • 学校网站下载零基础学it从哪方面学起
  • 自己建设淘宝客网站需要备案么东莞seo网络推广
  • 杭州广告公司网站建设wordpress 插件作用
  • 做微信用什么网站wordpress 去掉80previous与 next81
  • 做名片去哪个网站it行业公司排名
  • 合肥网站建设团队网站制作实例教程
  • 博达网站建设流程中国新闻社招聘公示
  • GEE SCL掩膜高精度 NDVI 提取教程(10 米分辨率 + SCL 掩膜)——免费提供完整代码
  • 网站群建设公司排行榜网站后端用什么语言
  • 网站域名是网站架构吗邯郸网站建设怎么开发
  • 网站栏目一般有哪些广州市建设局网站
  • 代码随想录 Q88.跳跃游戏
  • 网站开发制作步骤图微信公众号的微网站怎么做的
  • 策略梯度与值函数方法进行联合能解决什么问题
  • 无锡网站推广$做下拉去118cr广元 网站建设
  • 公司网站域名到期了去哪里缴费做刀网站
  • 爱站网排行榜wordpress用户角色权限管理
  • 网校网站毕业设计的方案毕设给学校做网站
  • 企业三要素验证API——企业数字化业务开展的保障
  • 专业系统网站百度产品推广
  • Android/Linux的FEC浅析
  • 网站开发好还是app好百度问答seo
  • 重庆巴南区网站建设如何做网站的seo