目录
大框架
接口实现
深层次拷贝问题(两次深拷贝)
大框架
为了与库里实现的更接近一些,先来看一下STL中是如何实现vector的(这里不是PJ版,PJ版稍微晦涩一点不容易理解,这里采用Linux下g++的版本)这里仅就大体框架做个简单介绍,其中复杂的概念不做过多解释。
首先是vector定义的模板参数T和Alloc,这是空间配置器是一种底层实现机制,使用角度不用管。
可以看到源码做了很多typedef,主要是对格式的控制和方便理解编写。其中将T* typedef成了iterator,这个要注意,后面protected 成员变量会用到。
这里成员变量就用到了刚刚typedef的iterator,实际上就是T* start ; T* finish; T* end_of_storage;
T就是实例化模板参数。好像与我们设想的变量不太一样,我们设想的应该是有size和capacity的,但是这里全部统一使用指针,其实只是改变了使用方式,本质还是一样的。
假设存在size(有效数据个数),capacity(容量),那么finish也可以表示成start+size,end_of_storage也可以表示成strat+capacity,本质是一样的。
后面是各个接口的实现,后面我们自己实现的时候具体说明,先把大框架搭建起来。
namespace bc
{
template<class T>
class vector
{
public:
typedef T* iterator;
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
为了避免和库里的冲突,将我们的实现代码封在命名空间内,库里的繁杂的typedef我们省略,只保留基本的T* 的重命名和成员变量表示。
接口实现
vector.h
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
namespace bc
{
template<class T>
class vector
{
public:
typedef T* iterator;
vector()
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{ }
//拷贝构造写法一
//vector(const vector<T>& v)
// : _start(nullptr)
// , _finish(nullptr)
// , _end_of_storage(nullptr)
//{
// reserve(size());
// for (auto& e : v) {
// push_back(e);
// }
//}
//拷贝构造现代写法
template<class InputIterator>
vector(InputIterator first, InputIterator last)
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while (first != last) {
push_back(*first);
first++;
}
}
vector(vector<T>& v)
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
vector(size_t n, const T& val= T())
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; ++i) {
push_back(val);
}
}
//函数重载
vector(int n, const T& val= T())
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n);
for (int i = 0; i < n; ++i) {
push_back(val);
}
}
~vector() {
delete[]_start;
_start = _finish = _end_of_storage = nullptr;
}
vector<T>& operator=(vector<T> v) {
swap(v);
return *this;
}
size_t size()const {
return _finish - _start;
}
size_t capacity()const {
return _end_of_storage - _start;
}
iterator begin() {
return _start;
}
iterator end() {
return _finish;
}
const iterator begin() const {
return _start;
}
const iterator end() const {
return _finish;
}
bool empty()const {
return _finish == _start;
}
void clear() {
_finish = _start;
}
void swap(vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
T& operator[](size_t pos) {
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const {
assert(pos < size());
return _start[pos];
}
void resize(size_t n, T val = T()) {
if (n > capacity()) {
reserve(n);
}
if (n > size() && n <= capacity()) {
while (_finish < _start + n) {
*_finish = val;
++_finish;
}
}
if (n <= size()) {
_finish = _start + n;
}
}
void reserve(size_t n) {
if (n > capacity()) {
size_t oldsize = size();
T* tmp = new T[n];
if (_start != nullptr) {
memcpy(tmp, _start, sizeof(T) * oldsize);
/*for (size_t i = 0; i < oldsize; ++i) {
tmp[i] = _start[i];
}*/
delete[]_start;
}
_start = tmp;
_finish = tmp + oldsize;
_end_of_storage = _start + n;
}
}
void push_back(const T& x) {
if (_finish == _end_of_storage) {
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
}
*_finish = x;
++_finish;
}
void pop_back() {
assert(!empty());
_finish--;
}
//迭代器失效
iterator insert(iterator pos, const T& x) {
assert(pos >= _start);
assert(pos < _finish);
if (_finish == _end_of_storage) {
size_t len = pos - _start;
size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
reserve(newcapacity);
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;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
下面是我给出的测试用例:
vector.h
void Test1() {
vector<int>a;
a.push_back(1);
a.push_back(1);
a.push_back(1);
a.push_back(1);
a.push_back(1);
cout << a.capacity() << endl;
for (size_t i = 0; i < a.size(); ++i)
{
cout << a[i] << " ";
}
cout << endl;
vector<int>::iterator it = a.begin();
while (it != a.end()) {
cout << (*it) << " ";
it++;
}
cout << endl;
//a.resize(2);
for (auto e : a) {
cout << e << " ";
}
cout << endl;
cout << a.empty() << endl;
a.pop_back();
for (auto e : a) {
cout << e << " ";
}
cout << endl;
}
void Test2() {
vector<int> a;
a.push_back(1);
a.push_back(1);
a.push_back(1);
a.push_back(1);
for (auto e : a) {
cout << e << " ";
}
cout << endl;
a.insert(a.begin(), 0);
for (auto e : a) {
cout << e << " ";
}
cout << endl;
vector<int>::iterator it = find(a.begin(), a.end(), 1);
if (it != a.end()) {
a.insert(it, 2);
}
for (auto e : a) {
cout << e << " ";
}
cout << endl;
(*it)++;
for (auto e : a) {
cout << e << " ";
}
cout << endl;
}
void Test3() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
while (it != v.end()) {
if ((*it) % 2 == 0) {
it = v.erase(it);
}
else {
++it;
}
}
for (auto e : v) {
cout << e << " ";
}
cout << endl;
}
void Test4() {
vector<int> v;
v.push_back(1);
v.push_back(1);
v.push_back(1);
v.push_back(1);
vector<int> a(v);
for (auto e : a) {
cout << e << " ";
}
cout << endl;
vector<int> b;
b = a;
for (auto e : b) {
cout << e << " ";
}
cout << endl;
vector<int>c(10, 1);
for (auto e : c) {
cout << e << " ";
}
cout << endl;
}
void Test5()
{
vector<vector<int>> vv;
vector<int> v(4, 1);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
vv.push_back(v);
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
cout << vv[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
}
test.cpp
#include"vector.h"
int main()
{
bc::Test1();
bc::Test2();
bc::Test3();
bc::Test4();
bc::Test5();
/*vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
std::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;*/
return 0;
}
以上是常用接口的大致实现,基本功能包含在内,还有些不太常用的接口没有实现。
深层次拷贝问题(两次深拷贝)
在测试vector<vector<int>>二维数组的时候,发现一个奇怪的现象:
当创建好vector<vector<int>> vv模型,并创建出内置数组vector<int> v (4,1)后,此时想要设定行数,也就是通过push_back插入行实现时,如果插入4个以内,没有问题,超过4个就会报错。
由此我们可以判定问题出在扩容上,具体我画图来解释。
对于vector<vector<int>>模型要有一定理解,外部的大类型是vector<>,< >里面的类型是vector<int>,所以先从内部开始创建,vector<int> v(4,1) 构造一个{1,1,1,1}的一维数组,里面构建好了,在外面通过push_back插入,相当于插入一个一个这样的一维数组,这样就构建了二维数组,push_back多少次就相当于有多少行,里面4相当于列。
vector<int> v 数组里面的元素是int类型的,外面通过_start 指针指向v数组,指针是int* 类型的,一个指针指向一个v数组,外面还有一个_start指针指向整个vector<vector<int>>,指针类型是vector<int>*
建好二维数组后来看扩容问题,当调用reserve时,需要开辟新空间tmp,用memcpy将数据传给tmp 。
而memcpy是按内存将数据拷贝给tmp的,这里的拷贝是浅拷贝,这就会导致一个问题,tmp最后也会指向_start 空间,
然后memcpy结束就是delete[]_start,delete会调用析构函数将_start空间释放,这下tmp指向的空间get不到了,tmp就是野指针了。
所以要想解决这个问题,就要解决内层浅拷贝的问题,将其改为深拷贝,这样外层tmp对象深拷贝,里层tmp数据也进行深拷贝,就不会出问题了。
解决方案:1、不用memcpy,利用operator = 重载函数将数据一个一个拷贝给tmp
void reserve(size_t n)
{
if (n > capacity())
{
size_t oldSize = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T)*oldSize);
for (size_t i = 0; i < oldSize; ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
_finish = tmp + oldSize;
_endofstorage = _start + n;
}
}
tmp[i] = _start[i] 将_start[i] 一个一个拷贝给tmp[i],这里调用operator= 是深拷贝。
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
但是这种方法缺点是效率太低。在C++11 中会有更好的方式解决上述问题。
以上就是深层拷贝的内容,关于底层实现还有其他不同版本,但因为复杂度等原因我们是按Linux g++使用的库实现的,感谢浏览!