容器是用來管理某一類對象的集合。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;
}