文章目录
- vector源码的内容:
- 成员变量
- 默认构造函数
- 构造函数1-无参构造
- 构造函数2 -使用n个相同的值构造
- 构造函数3-使用迭代器区间构造
- 拷贝构造函数
- **传统写法**
- 现代写法
- 赋值重载函数opeartor=
- 传统写法
- 现代写法
- 析构函数
- 迭代器
- begin & end
- 任意类型vector容器迭代器通用遍历方式:
- 容量相关的函数
- reserve
- resize
- capacity
- size
- empty
- 增删查改的函数
- push_back
- pop_back
- insert
- erase
- swap
- 访问
- operator[]
- 接口函数
- vector.h
- 测试代码
- 关于memcpy函数设计深拷贝
- 结论:
vector源码的内容:
start:指向第一个位置的指针
finish:最后一个位置的下一个位置的指针
End_of_storage:指向最后一个空间的下一个位置
size = finish - start capacity = end_of_stroage - start
我们直接使用new即可
成员变量
_start指向容器的起始位置,_finish指向容器当中有效数据的下一个位置,_endofstorage指向整个容器的结尾位置,
template<class T>//参数模板
class vector
{
public:
typedef T* iterator;//普通迭代器
typedef const T* const_iterator;//const迭代器
private:
iteartor _start;
iterator _finish;
iterator _endofstorage;
};
默认构造函数
构造函数1-无参构造
在初始化列表中,将三个指针置空
//构造函数-无参构造
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
构造函数2 -使用n个相同的值构造
先使用reserve函数将容器容量先设置为n,然后使用push_back函数尾插n个值为val的数据到容器当中即可,
注意:要先在初始化列表把三个指针置空,否则后序reserve的时候就会有问题!
//构造函数2-使用n个值初始化
vector(size_t n, T val)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
//先开辟n个空间
reserve(n);//内部会帮我们改指针的指向
// 放入数据
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
注意:
1)该构造函数知道其需要用于存储n个数据的空间,所以最好用reserve函数一次性开辟好空间,避免调用push_back函数时需要增容多次,导致效率降低,
2)该构造函数还需要实现重载函数,否则下面的代码会被认为是迭代器区间的构造
Mango::vector<int> v(2,3); //会被认为是迭代器区间的构造
而如果被认为是迭代器区间的构造, 因为要对迭代器进行解引用,而int类型不能解引用,所以发生报错!
补充两个重载函数
vector(long n, const T& val)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
reserve(n); //调用reserve函数将容器容量设置为n
for (size_t i = 0; i < n; i++) //尾插n个值为val的数据到容器当中
{
push_back(val);
}
}
vector(int n, const T& val)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
reserve(n); //调用reserve函数将容器容量设置为n
for (int i = 0; i < n; i++) //尾插n个值为val的数据到容器当中
{
push_back(val);
}
}
构造函数3-使用迭代器区间构造
存在下面的构造方式
string s1("hello");
vector<char> v(s1.begin(), s1.end());
-
vector支持使用一段迭代器区间进行对象的构造,
-
该迭代器区间可以是其他容器的迭代器区间,也就是说该函数接收到的迭代器的类型是不确定的
-
我们这里需要将该构造函数设计为一个函数模板,将该迭代器区间的数据一个个尾插到容器当中即可,
template<class InputIterator>//函数模板
//迭代器区间 左闭右开[first,last)
vector(InputIterator first, InputIterator last)
//指针需要初始化为空,否则指针是随机值,push_back的时候发生增容reserve可能出错,
:_start(nullptr) , _finish(nullptr) , _endofstorage(nullptr)
{
while (first != last)
{
//把迭代器区间的值拷贝到此时的调用对象中
push_back(*first);
first++;
}
}
拷贝构造函数
//v2(v1)
vector(const vector<T>& v)
:_start(nullptr) , _finish(nullptr), _endofstorage(nullptr)
{
_start = new T[v, capacity()];
memcpy(_start, v._start, sizeof(T) * v.size());
//注意我们要手动的更改_endofstorage和_finish的指向
_endofstorage = _start + v.capacity();
_finish = _start + v.size();
}
上述代码是可能导致问题的!
1.memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中,
2.如果拷贝的是内置类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝
所以不能使用memcpy进行拷贝! 如果容器中存的是string类型等其它自定义类型,就会发生错误
解决办法
memcpy的思路不完美,我们应该使用for循环,把原来容器的内容尾插到现在调用赋值重载函数的容器中.
如果是容器中存的是string类型,就会去调用string的赋值重载函数(是深拷贝)
传统写法
拷贝构造的传统写法的思想是我们最容易想到的:先开辟一块与该容器大小相同的空间,然后将该容器当中的数据一个个拷贝过来即可,最后更新_finish和_endofstorage的位置即可,
//传统写法
vector(const vector<T>& v)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
_start = new T[v.capacity()]; //开辟一块和容器v大小相同的空间,_start指向新空间的起始位置
for (size_t i = 0; i < v.size(); i++) //将容器v当中的数据一个个拷贝到当前容器
{
_start[i] = v[i];
}
//处理两个指针的位置
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
现代写法
使用范围for (C++11) 对容器进行遍历,在遍历过程中将容器v中存储的数据一个个尾插过来即可,
vector(const vector<T>& v)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
reserve(v.capacity()); //调用reserve函数将容器容量设置为与v相同
for (const auto& e : v) //将容器v当中的数据一个个尾插过来
{
push_back(e);
}
}
注意: 在使用范围for对容器v进行遍历的过程中,变量e就是每一个数据的拷贝,然后将e尾插到构造出来的容器当中,就算容器v当中存储的数据是string类,在e拷贝时也会自动调用string的拷贝构造(深拷贝),所以也能够避免出现与使用memcpy时类似的问题
现代写法2:
复用拷贝构造函数,构造一个临时容器,然后二者进行交换
vector(const vector<T>& v) // 推荐
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);//this->swap(tmp);
}
赋值重载函数opeartor=
vector的赋值同样涉及深拷贝,所以同样不能使用memcpy拷贝数据,又因为要支持连续赋值 ,因为返回引用
传统写法
首先判断是否是给自己赋值,若是给自己赋值则无需进行操作,
若不是给自己赋值,则先开辟一块和容器v大小相同的空间,然后将容器v当中的数据一个个拷贝过来,最后更新_finish和_endofstorage的值即可,
//参数需要加const,否则const对象不能调用
vector<T>& operator=(const vector<T>& v)
{
// 防止自己给自己赋值
if (this != &v)
{
delete[] _start;//释放原来空间的内容
_start = new T[v.capacity()];//开辟和拷贝对象一样大小的空间
//此处不能使用memcpy,防止浅拷贝
for (size_t i = 0; i < v.size(); i++) //将容器v当中的数据一个个拷贝过来
{
_start[i] = v[i];
}
//更新指针的位置
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
}
return *this; //支持连续赋值 出了作用域不销毁,可以返回引用
}
现代写法
首先在右值传参时并没有使用引用传参,因为这样可以间接调用vector的拷贝构造函数,然后将这个拷贝构造出来的容器v与左值进行交换,此时就相当于完成了赋值操作,而容器v会在该函数调用结束时自动析构,
//这里参数不能加const修饰,因为要发生交换,此时是传值拷贝,v就是v2传参是拷贝构造出来的
//v1 = v2
vector<T>& operator=(vector<T> v)
{
//v是局部对象,出了作用域就调用析构函数销毁了,仅是用来交换v的值到v1中
swap(v); //交换这两个对象
return *this;
}
这种写法进行的也是深拷贝,只不过是调用的vector的拷贝构造函数进行的深拷贝,深拷贝构造出v,然后v和当前调用该函数的容器交换
析构函数
先判断该容器是否为空,若不为空,则先释放容器存储数据的空间,然后将容器的各个成员变量设置为空指针即可,
//析构函数
~vector()
{
//判断原来空间是否为空
if (_start)
{
delete[] _start;//释放空间
}
_start = _finish = _endofstorage = nullptr;
}
迭代器
vector的迭代器实际上就是容器当中所存储数据类型的指针
typedef T* iterator;//普通迭代器
typedef const T* const_iterator;//const迭代器
begin & end
begin函数返回容器的首地址,end函数返回容器当中有效数据的下一个数据的地址,
普通对象调用:可读可写
//普通迭代器 -左闭右开
iterator begin()
{
return _start;//返回容器的起始位置
}
iterator end()
{
return _finish; //返回容器当中最后一个数据的下一个数据的位置
}
const对象调用:只读
//const迭代器 -左闭右开
const_iterator begin() const
{
return _start;//返回容器的起始位置
}
const_iterator end() const
{
return _finish;//返回容器当中最后一个数据的下一个数据的位置
}
任意类型vector容器迭代器通用遍历方式:
template<class T> //等价于:template<typename T>
void PrintVector(const vector<T>& v)
{
//取类模板的内嵌类型,类模板没有实例化,编译器不会去找
//前面加typename,告诉编译器这个是一个类型
typename vector<T>::const_iterator it = v.begin();//前面需要加typename,否则报错
//简写:由auto编译器自己推导
//auto it = v.begin();
while (it != v.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
int main()
{
vector<int> v(2, 3);
const vector<char> v2(6, 'x');
PrintVector(v);
PrintVector(v2);
return 0;
}
编译器在编译时会自动将范围for替换为迭代器的形式 支持迭代器就支持范围for
//范围for进行遍历,传引用防止深拷贝
for (auto& e : v)
{
cout << e << " ";
}
cout << endl;
容量相关的函数
reserve
Bug代码:
//扩容
void reserve(size_t n)
{
// 确保是扩容
if (n > capacity())
{
T* tmp = new T[n];
//判断原空间是否为空
if (_start)
{
把原空间的内容拷贝到新空间,拷贝的是字节数!
//要记得是数据个数*每一个数据的大小 sizeof(T)
memcpy(tmp, _start, size()*sizeof(T));
//释放原空间
delete[] _start;
}
_start = tmp;
_finish = _start + size();
_endofstorage = _start + n;
}
}
原因是:扩容之后,_start的指向已经发生改变,不再指向的是原空间,所以 size()函数计算出来的大小已经失效了, 并且不建议采用memcpy进行拷贝
解决办法:提前保存原来旧空间的元素个数 + 使用for循环拷贝元素
//扩容
void reserve(size_t n)
{
// 确保是扩容
if (n > capacity())
{
size_t sz = size();//旧空间的元素个数
T* tmp = new T[n];
//判断原空间是否为空
if (_start)
{
//把原空间的数据拷贝到新空间
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
//释放原空间
delete[] _start;
}
_start = tmp;
_finish = _start + sz;
_endofstorage = _start + n;//reserve了n个空间
}
}
resize
1、当n大于当前的size时,将size扩大到n,扩大的数据为val,若val未给出,则默认为容器所存储类型的默认构造函数所构造出来的值,
2、当n小于当前的size时,将size缩小到n,
根据resize函数的规则,进入函数我们可以先判断所给n是否小于容器当前的size,若小于,则通过改变_finish的指向,直接将容器的size缩小到n即可,否则先判断该容器是否需要增容,然后再将扩大的数据赋值为val即可,
//T():匿名对象,调用T类型的默认构造函数,
//如果是string类型,val就是空串,如果是int,val就是0
void resize(size_t n, T val = T())
{
//case1:缩小空间
if (n < size())
{
_finish = _start + n;
}
else
{
//case2:增加数据&容量够
if (n <= capacity())
{
while (_finish < _start + n)
{
*_finish = val;
_finish++;
}
}
else
{
//case3:增加数据&容量不够->增容
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
}
注意: 在C++当中内置类型也可以看作是一个类,它们也有自己的默认构造函数,所以在给resize函数的参数val设置缺省值时,设置为T( )即可,
优化:
如果n> size() 先判断是否需要扩容,如果需要,则先开辟空间,然后再插入值, 不需要则直接插入值
//优化:
void resize(size_t n, T val = T())
{
//case1:缩小空间
if (n < size())
{
_finish = _start + n;
}
else
{
//检查是否需要扩容
if (n > capacity())
{
reserve(n);
}
//增加数据
while (_finish < _start + n)
{
*_finish = val;
_finish++;
}
}
}
capacity
//容量
size_t capacity()
{
return _endofstorage - _start;//指针-指针 返回当前容器最多能存多少个数据
}
size
获取vector中有效数据个数 _finish - _start 两个指针得到的就是有效数据个数
//数据个数
size_t size()
{
return _finish - _start;//返回有效数据个数
}
empty
判断vector是否为空 如果_start和_finsh的指向相同, 说明容器内没有元素,就是空
//判空
bool empty() const
{
return _start == _finish;
}
增删查改的函数
push_back
要尾插数据首先得判断容器是否已满,若已满则需要先进行增容,然后将数据尾插到_finish指向的位置,再将_finish++即可,
//尾插
//传过来的参数不作修改,可以const修饰+传引用
void push_back(const T& x)
{
//判断空间是否满了
if (_finish == _endofstorage)
{
//扩容
size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newcapacity);
}
//把元素放在_finish位置
*_finish = x;
_finish++;
}
pop_back
尾删数据之前也得先判断容器是否为空,若为空则做断言处理,若不为空,则将_finish–-即可,
//尾删
void pop_back()
{
//保证容器不为空
assert(!empty());
_finish--;//_finish指针前移
}
insert
insert函数可以在所给迭代器pos位置插入数据, 在插入数据前先判断是否需要增容,然后将pos位置及其之后的数据统一向后挪动一位,最后将数据插入到pos位置即可,
注意:如果发生了增容,pos是指向原空间的,而_start是指向新空间了, 所以我们先记录_start和pos的相对距离,如果发生了增容的情况,就需要修正pos位置
//这里我们是不需要返回值的!
void insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
//插入之前:检查是否需要扩容
if (_finish == _endofstorage)
{
size_t len = pos - _start;//记录pos与_start之间的相对距离
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;//2倍扩容
reserve(newcapacity);
//更新pos在新空间的位置,解决增容后pos失效的问题
pos = _start + len;
}
iterator end = _finish - 1;
//把pos位置的值往后移动
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = x;//放到pos指向的位置
++_finish;//数据个数增加一个,_finish后移
}
注意: 若需要增容,则需要在增容前记录pos与_start之间的间隔,然后通过该间隔确定在增容后的容器当中pos的指向,否则pos还指向原来被释放的空间
erase
erase函数可以删除所给迭代器pos位置的数据,在删除数据前需要判断容器是否为空,若为空则需做断言处理,删除数据时直接将pos位置之后的数据统一向前挪动一位,将pos位置的数据覆盖即可
iterator erase(iterator pos)
{
//容器为空则断言
assert(!empty());
//把pos位置后面的数据往前挪动覆盖pos位置
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
it++;
}
--_finish;//删除了一个数据,finish--
return pos;//返回的是原来删除的数据的下一个数据的迭代器
}
注意:erase函数返回的是原来删除数据的下一个数据的迭代器,主要是为了迭代器失效后返回下一个位置进行处理
vector的insert和erase都可能导致迭代器失效,但是我们实现的时候,insert不返回值,erase返回删除位置的下一个迭代器 ,insert迭代器失效了,就不需要再使用啦! erase迭代器失效了,就接收返回值
swap
swap函数用于交换两个容器的数据,我们可以直接调用std库当中的swap函数将两个容器当中的各个成员变量进行交换即可,
//v1.swap(v2)
void swap(vector<T>& v)
{
::swap(_start, v._start);
::swap(_finish, v._finish);
::swap(_endofstorage, v._endofstorage);
}
注意: 在此处调用库当中的swap需要在swap之前加上::
(作用域限定符),告诉编译器这里优先在全局范围寻找swap函数,否则编译器会认为你调用的就是你正在实现的swap函数(就近原则),
访问
operator[]
vector的底层是数组,支持我们使用“下标+[ ]”的方式对容器当中的数据进行访问,实现时直接返回对应位置的数据即可,
//[]运算符重载
//可读可写版本-普通对象调用
T& operator[](size_t pos)
{
//保证位置的合法性
assert(pos < size());
return _start[pos]; //返回的对应数据-可读可写
}
//只读版本-const对象调用
const T& operator[](size_t pos) const
{
//保证位置的合法性
assert(pos < size());
return _start[pos];//返回的对应数据-只读 const修饰
}
注意: 重载运算符[ ]时需要重载一个适用于const容器的,因为const容器通过“下标+[ ]”获取到的数据只允许进行读操作,不能对数据进行修改,
接口函数
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace Mango
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//默认成员函数
vector();
vector(size_t n, const T& val);
vector(int n, const T& val);
vector(long n, const T& val);
template<class InputIterator>
vector(InputIterator first, InputIterator last);
vector(const vector<T>& v);
vector<T>& operator=(const vector<T>& v);
~vector();
//迭代器相关函数
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
//容量和大小相关函数
size_t size()const;
size_t capacity()const;
void reserve(size_t n);
void resize(size_t n, const T& val = T());
bool empty()const;
//修改容器内容相关函数
void push_back(const T& x);
void pop_back();
void insert(iterator pos, const T& x);
iterator erase(iterator pos);
void swap(vector<T>& v);
//访问容器相关函数
T& operator[](size_t i);
const T& operator[](size_t i)const;
private:
iterator _start; //指向容器的头
iterator _finish; //指向有效数据的尾
iterator _endofstorage; //指向容器的尾
};
}
为了防止和库中的vector函数重名,模拟实现时,应该放到我们自己的命名空间中
vector.h
#pragma once
namespace Mango
{
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)
{
_start = new T[v.capacity()];
_finish = _start + v.size();
_endofstorage = _start + v.capacity();
memcpy(_start, v._start, v.size()*sizeof(T));
}
*/
// 一个类模板的成员函数,又可以是一个函数模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
while (first != last)
{
push_back(*first);
++first;
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
// v2(v1) 现代写法
//vector(const vector& v)
vector(const vector<T>& v) // 推荐
:_start(nullptr), _finish(nullptr), _endofstorage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);//this->swap(tmp);
}
// v1 = v3 现代写法
// vector& operator=(vector v)
vector<T>& operator=(vector<T> v) // 推荐
{
swap(v);
return *this;
}
~vector()
{
if (_start)
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const T& operator[](size_t i) const
{
assert(i < size());
return _start[i];
}
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstorage - _start;
}
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();//记录原来的_finish和_start的距离
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T)*size());
for (size_t i = 0; i < sz; ++i)
{
// T 是int,一个一个拷贝没问题
// T 是string, 一个一个拷贝调用是T的深拷贝赋值,也没问题
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
// _finish = _start + size(); size()的计算存在问题,所以使用下面的方式
_finish = _start + sz;
_endofstorage = _start + n;
}
}
void resize(size_t n, const T& val = T())
{
if (n < size())
{
_finish = _start + n;
}
else
{
if (n > capacity())
{
reserve(n);
}
while (_finish != _start+n)
{
*_finish = val;
++_finish;
}
}
}
iterator insert(iterator pos, const T& x)
{
assert(pos >= _start);
assert(pos <= _finish);
// 满了就扩容
if (_finish == _endofstorage)
{
// 扩容会导致pos失效,扩容需要更新一下pos
size_t len = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len;
}
iterator 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 begin = pos + 1;
while (begin < _finish)
{
*(begin-1) = *begin;
++begin;
}
--_finish;
return pos;
}
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
// 扩容
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x;
++_finish;
}
void pop_back()
{
assert(_finish > _start);
--_finish;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
测试代码
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);
v.push_back(6);
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";//1 2 3 4 5 6
}
cout << endl;
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";//1 2 3 4 5 6
++it;
}
cout << endl;
for (auto e : v)
{
cout << e << " ";//1 2 3 4 5 6
}
cout << endl;
}
void test_vector3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int> v2(v1);
for (auto e : v2)
cout << e << " ";//1 2 3 4
cout<<endl;
vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v1 = v3;
for (auto e : v1)
cout << e << " ";//10 20 30
cout << 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>::iterator it = find(v1.begin(), v1.end(), 2);
if (it != v1.end())
{
// 如果insert中发生了扩容,那么会导致it指向空间被释放
// it本质就是一个野指针,这种问题,我们就叫迭代器失效
v1.insert(it, 20);
}
for (auto e : v1)
{
cout << e << " ";//1 20 2 3 4
}
cout<<endl;
}
void test_vector5()
{
// 三种场景去测试
// 1 2 3 4 5 -> 正常
// 1 2 3 4 -> 崩溃
// 1 2 4 5 -> 没删除完
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(4);
v1.push_back(5);
for (auto e : v1)
cout << e << " "; //1 2 4 5
// 要求删除v1所有的偶数
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
it = v1.erase(it);
else
++it;
}
for (auto e : v1)
cout << e << " "; //1 5
cout << endl;
}
void test_vector6()
{
vector<string> v;
v.push_back("111111111111111111111111");
v.push_back("111111111111111111111111");
v.push_back("1111111111");
v.push_back("1111111111");
v.push_back("1111111111");
for (auto& e : v)
{
cout << e <<endl;
}
cout<<endl;
}
关于memcpy函数设计深拷贝
- memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
- 如果拷贝的是自定义类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且
自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝
假设vector存的是string对象
->
结论:
如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是
浅拷贝,否则可能会引起内存泄漏甚至程序崩溃,