(关于前几篇文章埋下的坑)
自作者开始浅谈STL以来,虽然只出过STL的内存管理方式,但是讲到的东西真的远远不及他里面的内容,只是很浅的谈论里面的一些做法而已。而真正的内存管理,我也在前面几篇文章中说到,这个是一个非常大的话题,或许在座的读者是刚刚经历了面试的应届生,或许您的面试官会或多或少的问过关于一些c++内存管理的事情,但是网上很多很多的答案都是非常肤浅的回答与描述。如果作者在接下来的几个月中时间充裕的话,我会真的下定决心,我们来浅谈一下c++的内存管理,这个真的是一个大话题,或者作者也把控不住。(不要问我为什么要那么执着于内存、那么推崇c/c++,那是我认为,每一个程序员,真正的功底,也许您现在不太看好c/c++,认为java才是天下无敌,那么您直接退出此文章即可。)
前述
这篇文章也需要一些前置知识:您至少得使用过STL的一种容器,会c++的基本语法,什么叫模板等。
在讲我们接下来的主题之前,我们来探讨一件事情,在我们没有接触到容器之前,我们一般在定义一个一定容量的数据单元的时候,首先想到的,那便是数组,但是数组有一个较大的缺点,那就是一旦我们确定下来数组的大小,那便不会再改变,如果我们还想继续扩充容量,我们只好重新定义一个,然后赋值,这显然是在容器推出之前,我们普遍的做法,即使在c99之后,推出了可变长数组,通常可变长数组一般不占大小,但是一旦确定大小之后,也不能在改变,这个也没有根本的解决我们的问题。有没有一种方法,可以让我定义一个数据单元,我可以一直在里面放东西,我不需要提前设置他的大小,只要我还在使用,我就可以一直放东西?
当然有,而且今天我们就要讲其中的一种,那便是vector。
vector的自我介绍
vector,顾名思义,那便是向量嘛,所谓向量,那便是,有大小,有方向的量,vector也是向量,他的大小可变,当然它也有方向:左闭右开嘛。它相对于array而言,array一旦确定了大小,便不可改变,是一个静态的量,而vector可以变化,是一个动态的量,随着我们新元素的加入,其内部空间会随着其内部动态增长机制不断的变化。
vector内部实现(常用的)
//vector的第二参数代表他的内存分配器alloc,这个作者已经讲过,也展示过其内部实现与他的原理,见以前的文章
//第二参数可以改变,这个只是它默认的空间分配器而已
template
class vector
{
public:
typedef T value_type;//元素类型
typedef value_type* pointer;//指针
typedef value_type* iterator;//迭代器,可见,vector里的迭代器其实就是指针,但不能说迭代器济就是指针
typedef value_type& reference;//引用
typedef size_t szie_type;//大小
typedef ptrdiff_t difference_type;//表示指针之间的距离的一种安全方式
protected:
typedef simple_allocdata_allocator;//默认分配器
iterator start;//正在使用的空间头部
iterator finish;//当前最后一个元素的下一个位置
iterator end_of_storage;//整个空间的尾部
//在position指定的位置上插入一个元素
void insert_aux(iterator position, const T& x);
void deallocate()
{
if (start)
{
data_allocator::deallocate(start, end_of_storage - start);
}
}
void fill_initialize(size_type n, const T& value)
{
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
iterator allocate_and_fill(size_type n, const T& value)
{
iterator result = data_allocator::allocate(n);
uninitalized_fill_n(result, n, value);//从result开始,用value初始化,大小为n
return result;
}
public://常用函数
iterator begin() { return start;}
iterator end() { return finish;}
size_type size() { return size_type(end() - begin());}
size_type capacity() const { return end_of_storage - begin(); }
bool empty() const { return begin() == end();}
reference operator[] (size_type n) { return *(begin() + n);}
vector():start(0), finish(0),end_of_storage(0){}
vector(size_type n, const T& value){ fill_initialize(n, value);}
vector(int n, const T& value){ fill_initialize(n, value);}
vector(long n, const T& value){ fill_initialize(n, value);}
explicit vector(size_type n){ fill_initialize(n, T());}//避免隐式转换
~vector()
{
destory(start, finish);//全局函数,从start到finish,调用vector的析构函数
deallocate();
}
reference front() { return *begin();}
reference back() { return *(end() - 1);}
void push_back(const T& x)//插入元素
{
if (finish != end_of_storage)
{
construct(finish, x);//在finish的位置上调用placenew;
++finish;
}
else
{
insert_aux(end(),x);
}
}
void pop_back()
{
--finish;
destory(finish);//调用finish这个位置上的析构函数
}
//删除某一位置上的元素
iterator erase(iterator position)
{
if (position + 1 != end())
{
copy(position + 1, finish, position);//position之后的元素wangle前移动
}
--finish;
destory(finish);
return position;
}
void resize(size_type new_szie, const T& value)
{
if (new_szie < size())
{
erase(begin() + new_szie, end());//这个是算法库中函数,移除从a->b之间的元素
}
else
{
insert(end(), new_size - size(), value);//算法库中函数,插入从A->B之间,用value初始化
}
}
··//函数重载,仔细看区别
void resize(size_type new_size)
{
return (resize(new_szie, T());
}
};
template
void vector::insert_aux(iterator position, const T& x)
{
if (finish != end_of_stroage)//当前空间还有剩余
{
constrcut(finish,*(finish - 1));
++finish;
T x_copy = x;
copy_backward(position,finish - 2, finish - 1);
*position = x_copy;
}
else//当前空间没有剩余
{
const size_type old_szie = size();
const size_typoe len = old_size != 0 ? 2*old_size : 1;//两倍扩展,在linux中是1.5倍
iterator new_start = data_alloctor::allocate(len);
iterator new_finish = new_start;
//回滚机制
try{
//将旧的vector中的元素拷贝到新的vector中
new_finish = uninitialzed_copy(start, position, new_start);
construct(new_satrt,x);//设初值
++finish;
new_finish = uninitialzed_copy(position, finish, new_start);//安插点的原内容也拷贝过来
}
catch(...)
{
//一旦发现有异常,那么直接回滚,将申请的空间全部毁掉
destory(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
destory(begin(), end());
deallocate();
start = new_start;
finish = new_finish;
end_of_storage = new_start + len;
}
}
总结
看这个,比你看面经直接了当的多吧~(作者很讨厌看面经,这种感觉就想拾人牙慧),与其去看别人的总结,倒不如去看真正的源码。有些函数在前几篇文章中,有其声明与实现,没有的,我已经补充其作用了!