一、vector的简单使用以及耗时测试:
#include
#include
#include//qsort bsearch NULL
#include
using namespace std;
const int SIZE = 100000;
int main()
{
vectorvec;
clock_t start = clock();//ms
for (int i = 0; i < SIZE; ++i)
{
vec.push_back(rand() % SIZE);
}
cout << "插入100000个元素耗时: " << (clock() - start) << endl;
cout << "当前的有效元素个数: " << vec.size() << endl;
cout << "第一个元素是: " << vec.front() << endl;
cout << "最后一个元素是: " << vec.back() << endl;
cout << "第一个元素的地址是: " << &vec[0] << endl;
cout << "vector的起始地址是: " << vec.data() << endl;
cout << "插入一个元素99999" ; vec.push_back(99999);
cout << "当前最后一个元素是: " << vec.back() << endl;
cout << "最终vector的容量是: " << vec.capacity() << endl;
vec.push_back(999);
// //挨个查找
// auto it = ::find(vec.begin(), vec.end(), 999);
// if (it != vec.end())
// {
// cout << "找到了" << *it << endl;
// }
// else
// {
// cout << "not find" << endl;
// }
return 0;
}
执行结果:
插入100000个元素耗时: 63
当前的有效元素个数: 100000
第一个元素是: 41
最后一个元素是: 1629
第一个元素的地址是: 00A3C040
vector的起始地址是: 00A3C040
插入一个元素99999当前最后一个元素是: 99999
最终vector的容量是: 138255
请按任意键继续. . .
1.底层是一个动态开辟的一维数组 ,是一个指针T *array;
2.默认实例化时不分配内存,size=0,即vectorvec;实际上并没有给分配内存
3.增长时是以二倍的速度增长0->1->2->4->8... ;
对应增长需求比较大的,这样效率明显低下,所以我们有reserve()来提前开辟好内存,
如需增长直接在在提前开辟好的内存上构造对象即可;
4.扩容的时候,需要先找好一块内存,然后将原来vector中的数据copy到新的二倍的vector上,
释放原来的内存,调整三个指针的位置,指向新的内存上来;
5.线性的容器都会提供[]运算符重载函数,包括array和deque(deque实际不是线性的,看起来是),
这样访问的时间复杂度明显是O(1),具体实现后面说;
6.不提供前插或者前删的操作,因为挪动元素实在会产生太大的开销(大量的构造和析构函数调用)
只有push_back()和pop_back(),insert()
7.resize()是扩容,期间会调用构造函数,也会进行释放内存和重新开辟内存,而reserve()只是进行预留空间,不进行构造对象
8.尽管具有[],查询效率比较高O(1),但是往中间插入效率依然会比较低了
9.at()函数和[]函数使用可以说是一模一样. 都是为了访问对应index中存储的数据,
如果index大于vector的size(越界). 两者都是抛出out_of_range的exception.
new如果失败了,也会抛出bad_alloc的异常。
三、找工作时遇到的几个问题:
1.面试官问:你知道为什么vector是以2倍增长的?
?????当时内心想着人家就这样设计的,我怎么知道设计者内心怎么想的
后来查阅了一些资料,确实有某种说法,不过也有的是用1.5倍增长实现的,emmmm
有人回答说:vector最关键在于查询,使用移位(2的幂)直接得到哈希链以及节点长度,
然后相减直接得到键值,复杂度为O(2),性能近似于数组,插入删除可动态,
这就是vector设计的基本目的。
显然,增长的倍数不可能很大,也不会比 1 小,那么,它的最佳上限是多少呢?
如果以 大于2 倍的方式扩容,下一次申请的内存会大于之前分配内存的总和,
导致之前分配的内存不能再被使用。所以,最好的增长因子在 (1,2)之间。
2.swap()方法的实现:
void swap(_Myt& _Right)
{ // exchange contents with _Right
if (this == &_Right)
; // 同一个对象,什么也不做
else if (this->_Getal() == _Right._Getal())
{ //如果是相同的分配器,那么就直接交换两个对象中指针的地址即可
this->_Swap_all(_Right);
_Swap_adl(this->_Myfirst, _Right._Myfirst);
_Swap_adl(this->_Mylast, _Right._Mylast);
_Swap_adl(this->_Myend, _Right._Myend);
}
else if (_Alty::propagate_on_container_swap::value)
{ // swap allocators and control information
//交换分配器和对象的指针信息
this->_Swap_alloc(_Right);
_Swap_adl(this->_Myfirst, _Right._Myfirst);
_Swap_adl(this->_Mylast, _Right._Mylast);
_Swap_adl(this->_Myend, _Right._Myend);
}
else
{ // 容器是不匹配的,不能进行交换
#if _ITERATOR_DEBUG_LEVEL == 2
_DEBUG_ERROR("vector containers incompatible for swap");
#else /* ITERATOR_DEBUG_LEVEL == 2 */
_XSTD terminate();
#endif /* ITERATOR_DEBUG_LEVEL == 2 */
}
}
所以,我们很明了可以看到,两个vector对象,在进行交换的时候
并不是挨个的拷贝,而是直接将两个对象的指针交换,有点浅拷贝的意思,实际上就是。
1.如果分配器分配的同一个类型:交换三根指针指向即可
2.如果分配器不一样:得交换对象的使用的分配器Swap_alloc(),然后交换三根指针
3.ERROR
这样做效率明显提高!!!
3.使用的时候哪些方法可能会抛出异常:
1.使用到new() 的地方 C++中new失败不用NULL判断,而是捕获异常,bad_alloc异常
2.at()和operator[]()根据下标访问来元素,当下标越界时 会抛出out_of_range越界的异常
4.分配allocator的实现:
见下面代码
5.如何缓解二倍增长带来的效率问题呢:
resize()调用会析构释放原来的内存,开辟二倍的空间大小,
并且还有将原来的对象拷贝构造到新的内存空间上,效率问题显而易见
所以,我们缓解该问题,实现一个方法叫reserve(),预留空间,提前开辟内存
这样到时候直接去预留的空间构造函数,就不用多次resize()。
四、之前自己实现的一个vector:
我简单的实现一个vector,不用和标准库多相似,只要知道设计思想即可。
template
class myallocator
{
public:
//构造
void construct(void *ptr, const T &val)
{
//定位new,表示在ptr指向的内存,构造一个值为val的对象
new (ptr)T(val);
}
//析构
void destroy(T *ptr)
{
ptr->~T();
}
//开辟内存
T* allocate(size_t size)
{
//malloc
return (T*)malloc(size);
}
//释放内存
void deallocate(void *ptr)
{
free(ptr);
}
};
template
class Vector
{
public:
//默认构造的vector,底层没分配过内存0
Vector() :mpVec(NULL), mSize(0), mCur(0){}
//size表示初始的内存大小,val表示内存初始值
Vector(int size, const T &val = T())
:mSize(size), mCur(size)
{
mpVec = _allocator.allocate(mSize * sizeof(T));
for (int i = 0; i < mSize; ++i)
{
_allocator.construct(mpVec + i, val);
}
}
//拷贝构造
Vector(const Vector &src)
:mSize(src.mSize), mCur(src.mCur)
{
mpVec = _allocator.allocate(sizeof(T)*mSize);
for (int i = 0; i < mCur; ++i)
{
_allocator.construct(mpVec + i, src.mpVec[i]);
}
}
//operator=
Vector& operator=(const Vector &src)
{
if (this == &src)
return *this;
for (int i = 0; i < mCur; ++i)
{
_allocator.destroy(mpVec + i);
}
_allocator.deallocate(mpVec);
mpVec = _allocator.allocate(sizeof(T)*mSize);
for (int i = 0; i < mCur; ++i)
{
_allocator.construct(mpVec + i, src.mpVec[i]);
}
return *this;
}
~Vector()
{
for (int i = 0; i < mCur; ++i)
{
_allocator.destroy(mpVec + i);
}
_allocator.deallocate(mpVec);
mpVec = NULL;
}
//末尾添加元素 push_front pop_front O(n)
void push_back(const T &val)
{
if (full())
reSize();
_allocator.construct(mpVec + mCur, val);
mCur++;
}
//末尾删除
void pop_back()
{
if (empty())
return;
--mCur;
_allocator.destroy(mpVec + mCur);
}
T front()const{ return mpVec[0]; }
T back()const{ return mpVec[mCur - 1]; }
bool full()const{ return mCur == mSize; }
bool empty()const{ return mCur == 0; }
T& operator[](int index){ return mpVec[index]; }
//内存以2倍方式增长
void reSize()
{
//默认的size==0
if (mSize == 0)
{
mpVec = _allocator.allocate(sizeof(T));
mSize = 1;
mCur = 0;
}
else
{
T *ptmp = _allocator.allocate(mSize * 2 * sizeof(T));
for (int i = 0; i < mCur; ++i)
{
_allocator.construct(ptmp + i, mpVec[i]);
}
mSize *= 2;
for (int i = 0; i < mCur; ++i)
{
_allocator.destroy(mpVec + i);
}
_allocator.deallocate(mpVec);
mpVec = ptmp;
}
}
int size()const{ return mCur; }
//定义当前容器的迭代器类型 ,遍历容器(遍历容器底层的数据结构)
class iterator
{
public:
iterator(T *p = NULL)
{
ptr = p;
}
bool operator!=(const iterator &it)
{
return ptr != it.ptr;
}
int operator-(const iterator &it)
{
return ptr - it.ptr;
}
void operator++()
{
++ptr;
}
T& operator*()
{
return *ptr;
}
private:
T *ptr; //本质上就是一根被泛化的指针
};
iterator begin(){ return iterator(mpVec); }
iterator end(){ return iterator(mpVec + mCur); }
private:
T *mpVec;//动态数组的起始地址,相当于first
int mSize;//容量
int mCur;//当前size
Allocator _allocator;
//也可以在这里添加一个end指针,这里命名有点不规范,影响不大
};
五、补充vector和array区别
vector:动态增长 动态开辟内存 可扩容 相对灵活
array:固定长度 不可扩容 灵活度差
参考链接:https://en.cppreference.com/w/cpp/container/vector