容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。
例子:
Vector:
向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
Vector 底层已经封装好了增删查改
void reserve(int size); // 预留函数
void push_back(const T &val); // 末尾添加元素
void pop_back(); // 末尾删除元素
bool empty()const; // 判空
bool full()const; // 判满
reserve只开空间 但是没有放东西进去
resize不仅给vector容器开辟size个空间,还放了size个东西进去了
出现空间配置器的原因
1.在开辟空间时,用new来构造对象会增加大量的无效对象.
所以应该把对象的内存开辟和对象构造分开 不能直接用new new 开辟内存 调用构造函数
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
};
int main()
{
vector<int> intvec(20);//默认开辟20个空间 还调用了构造函数 但是我们只要空间的其实
return 0;
}
2.在添加元素的时候应当是拷贝构造,而不是赋值重载(赋值重载是因为本来就存在了对象)
3.删除元素的时候不光是把指针–,还得把last指向的对象进行析构
4.在容器销毁时,析构的应当是有效对象,而不是把我构造出来的没用到的对象也析构掉了.
所以应该把对象的析构和内存释放分开 不能直接用delete delete先调用析构函数 再释放内存(free)
template<typename T>
void Vector<T>::reserve(int size)//预留函数
{
if (size <= msize)
return;
T *ptmp = new T[size];
for (int i = 0; i < mcur; ++i)
{
ptmp[i] = mpvec[i];
}
delete[]mpvec;
mpvec = ptmp;
msize = size;
}
... ...
int main()
{
// C++ STL 组件之一:容器
Test t1, t2, t3;
cout << "------------" << endl;
Vector<Test> vec;
vec.reserve(10); // 只想预留空间 不想创建那么多的对象
cout << "------------" << endl;
vec.push_back(t1);//添加元素想构造 而不是赋值操作
vec.push_back(std::move(t2));
vec.push_back(t3);
cout << "------------" << endl;
vec.pop_back();
cout << "------------" << endl;
解决方法:
1.destory()
从容器删除对象的时候要析构,但不能用delete,用destory(),指针指向的对象,调用它的析构.
容器有多少个元素就析构多少个(+i是要往后移动,mpvec就是首元素的地址)
template<typename T>
Vector<T>::~Vector()
{
for (int i = 0; i < mcur; ++i)
{
// 析构容器中有效的对象
malloctor.destroy(mpvec+i);
}
// 把容器内存释放掉
malloctor.deallocate(mpvec);
mpvec = nullptr;
}
2.construct()
在一个开辟好的内存上构造一个对象(这样就可以实现拷贝构造而不是赋值重载)
// 自定义一个容器的空间配置器
template<typename T>
struct myallocator
{
// 内存开辟
T* allocate(size_t size)
{
return (T*)malloc(size * sizeof(T));
}
// 内存释放
void deallocate(T *ptr)
{
free(ptr);
}
// 对象构造 1.new 2.new const 3.定位new
void construct(T *ptr, const T &val)
{
new (ptr) T(val); // 定位new
//在指定的地址ptr上构造了一个对象 传入val
}
// 对象析构
void destroy(T *ptr)
{
ptr->~T();//调用指针指向的对象的析构函数 析构函数可以手动调用
}
};
new的四种方法
1>new nothorow int(10)
抛异常的new,返回值是空指针
2>new const
3>new
4>new(ptr) T(obj)
定位new,在已经开辟好的内存上构造对象,ptr是指定内存的地址
空间配置器的定义
template<typename T>
struct myallocator
{
};
template<typename T=int,typename _Alloc=myallocator<T>>
//_Alloc是类型,所以myallocator这个模板名就要改为myallocator<T>类型名
class Vector
{
};
所以vector必须有两个参数 一个是传入的类型T 一个是空间配置器alloc
用户可以不自己提供空间配置器 用默认写好的模板空间配置器就好 typename Alloc=Allocator
template<typename T,typename Alloc=Allocator>
完整的vector空间配置器代码实现
/*
容器的空间配置器 allocator
作用:
对象的内存开辟和对象构造分离开
对象的析构和内存释放分离开
allocate : 内存开辟
deallocate : 内存释放
construct : 对象构造
destroy : 对象析构
*/
// 自定义一个容器的空间配置器
template<typename T>
struct myallocator
{
// 内存开辟
T* allocate(size_t size)
{
return (T*)malloc(size * sizeof(T));
}
// 内存释放
void deallocate(T *ptr)
{
free(ptr);
}
// 对象构造 1.new 2.new const 3.定位new
void construct(T *ptr, const T &obj)
{
new (ptr) T(obj); // 定位new
}
// 对象析构
void destroy(T *ptr)
{
ptr->~T();
}
};
// 类模板 vector 默认构造 size:0-1-2-4-8-16-32-64....
template<typename T = int,
typename _Alloc = myallocator<T>>
class Vector // 类的模板名字
{
public:
Vector();
~Vector();
Vector(const Vector<T> &vec);
Vector(Vector<T> &&vec);
void operator=(const Vector<T> &vec);
void operator=(Vector<T> &&vec);
void reserve(int size); // 预留函数
void push_back(const T &val); // 末尾添加元素
void pop_back(); // 末尾删除元素
bool empty()const; // 判空
bool full()const; // 判满
int size()const; // 返回vector容器元素的个数
void show()const; // 打印vector容器的所有元素
private:
T *mpvec;
int mcur;
int msize;
_Alloc malloctor; // 定义空间配置器对象
void resize(); // vector容器底层默认2倍扩容
};
template<typename T, typename _Alloc>
Vector<T, _Alloc>::Vector()
:mcur(0)
, msize(0)
, mpvec(nullptr) {}
template<typename T, typename _Alloc>
Vector<T, _Alloc>::~Vector()
{
for (int i = 0; i < mcur; ++i)
{
// 析构容器中有效的对象
malloctor.destroy(mpvec + i);
}
// 把容器内存释放掉
malloctor.deallocate(mpvec);
mpvec = nullptr;
}
template<typename T, typename _Alloc>
Vector<T, _Alloc>::Vector(const Vector<T>& vec)//左值拷贝构造
{
msize = vec.msize;//空间大小
mcur = vec.mcur;//现在存的元素个数
T *ptmp = malloctor.allocate(msize);//先申请
for (int i = 0; i < mcur; i++)
{
malloctor.construct(ptmp + i, vec.mpvec[i]);//
}
mpvec = ptmp;
}
template<typename T, typename _Alloc>
Vector<T, _Alloc>::Vector(Vector<T>&& vec)
{
mpvec = vec.mpvec;
vec.mpvec = nullptr;
mcur = vec.mcur;
msize = vec.msize;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::operator=(const Vector<T>& vec)
{
if (this == &vec)
{
return;
}
malloctor.deallocate(mpvec);//先释放内存
msize = vec.msize;//空间大小
mcur = vec.mcur;//现在存的元素个数
T * ptmp = malloctor.allocate(msize); //申请内存
for (int i = 0; i < mcur; i++)
{
malloctor.construct(ptmp + i, vec.mpvec[i]);//对象构造
}
mpvec = ptmp;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::operator=(Vector<T>&& vec)
{
if (this == &vec)
{
return;
}
malloctor.deallocate(mpvec);
msize = vec.msize;
mcur = vec.mcur;
mpvec = vec.mpvec;
vec.mpvec = nullptr;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::reserve(int size)
{
if (size <= msize)
return;
T *ptmp = malloctor.allocate(size);
for (int i = 0; i < mcur; i++)
{
malloctor.construct(ptmp + i, mpvec[i]);
// 析构容器中有效的对象
malloctor.destroy(mpvec + i);
}
// 把容器内存释放掉
malloctor.deallocate(mpvec);
mpvec = ptmp;
msize = size;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::push_back(const T & val)//尾部添加
{
if (full())
{
resize();
}
T *ptmp = mpvec;
malloctor.construct(ptmp + mcur, val);
mcur++;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::pop_back()//尾部删除
{
if (empty())
{
return;
}
T * ptmp = mpvec;
mcur--;
malloctor.destroy(ptmp + mcur);
}
template<typename T, typename _Alloc>
bool Vector<T, _Alloc>::empty() const
{
return mcur == 0;
}
template<typename T, typename _Alloc>
bool Vector<T, _Alloc>::full() const
{
return mcur == msize;
}
template<typename T, typename _Alloc>
int Vector<T, _Alloc>::size() const
{
return mcur;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::show() const
{
for (int i = 0; i < mcur; i++)
{
cout << mpvec[i] << " ";
}
cout << endl;
}
template<typename T, typename _Alloc>
void Vector<T, _Alloc>::resize()//扩容
{
if (msize > 0)
{
T *ptmp = malloctor.allocate(msize * 2);
for (int i = 0; i < msize; i++)
{
malloctor.construct(ptmp + i, mpvec[i]);
// 析构容器中有效的对象
malloctor.destroy(mpvec + i);
}
// 把容器内存释放掉
malloctor.deallocate(mpvec);
mpvec = ptmp;
msize *= 2;
}
else
{
mpvec = malloctor.allocate(1); // size * sizeof(T)
msize = 1;
mcur = 0;
}
}
class Test
{
public:
Test() :mptr(new int[10000])
{
cout << "Test()" << endl;
}
~Test()
{
delete[]mptr;
cout << "~Test()" << endl;
}
Test(const Test& src)
{
cout << "Test(const Test&)" << endl;
mptr = new int[10000];
memcpy(mptr, src.mptr, 10000 * 4);
}
void operator=(const Test& src)
{
cout << "operator=" << endl;
if (this == &src)
return;
delete[]mptr;
mptr = new int[10000];
memcpy(mptr, src.mptr, 10000 * 4);
}
private:
int *mptr;
};
int main()
{
// C++ STL 组件之一:容器
Test t1, t2, t3; //三次构造
cout << "------------" << endl;
Vector<Test> vec;
vec.reserve(10); // 只想预留空间
cout << "------------" << endl;
vec.push_back(t1); //拷贝构造
vec.push_back(std::move(t2)); //拷贝构造
vec.push_back(t3); //拷贝构造
cout << "------------" << endl;
vec.pop_back(); //一次析构
cout << "------------" << endl;
Vector<Test> vec2 = vec; //两次构造
//七次析构
return 0;
}