C++學習8-容器的空間配置器


容器是用來管理某一類對象的集合。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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章