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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章