c++简易vector实现

数组实现

源代码

#include<iostream>
#include<stdexcept>

template<typename T>
class vector{
public:
    vector(size_t size=10);
    vector(const vector& rhs);
    vector<T>& operator=(vector& rhs);
    ~vector();

    bool full() const;
    bool empty() const;
    size_t size() const;
    size_t capacity() const;
    
    void push_back(T& elm);
    void pop_back();
    T back();
private:
    T* first_;
    T* last_;
    T* end_;
    void expand();
    void copy_from(const vector& rhs);
};

template<typename T>
vector<T>::vector(size_t size)
:first_(new T[size])
,last_(first_)
,end_(first_+size)
{}

template<typename T>
vector<T>::vector(const vector& rhs)
{
    copy_from(rhs);
}

template<typename T>
vector<T>& vector<T>::operator=(vector& rhs)
{
    if(this!=&rhs)
    {
        delete []first_;
        copy_from(rhs);
    }
    return *this;
}

template<typename T>
vector<T>::~vector()
{
    delete []first_;
    first_=last_=end_=nullptr;
}

template<typename T>
bool vector<T>::full() const
{
    return last_==end_;
}

template<typename T>
bool vector<T>::empty() const
{
    return last_==first_;
}

template<typename T>
size_t vector<T>::size() const
{
    return last_-first_;
}

template<typename T>
size_t vector<T>::capacity() const{
    return end_-first_;
}

template<typename T>
void vector<T>::push_back(T& elm)
{
    if(full())
        expand();
    *last_++=elm;
}

template<typename T>
void vector<T>::pop_back()
{
    if(empty())
        throw std::out_of_range("vector is empty!");
    --last_;
}

template<typename T>
T vector<T>::back()
{
    if(empty())
        throw std::out_of_range("vector is empty!");
    return *(last_-1);
}

template<typename T>
void vector<T>::expand()
{
    size_t old_size=last_-first_;
    size_t new_size=old_size*2;
    T* first=new T[new_size];
    for(int i=0;i<old_size;i++)
        first[i]=first_[i];
    delete []first_;
    first_=first;
    last_=first_+old_size;
    end_=first+new_size;
}

template<typename T>
void vector<T>::copy_from(const vector& rhs)
{
    size_t size=rhs.last_-rhs.first_;
    first_=new T[size];
    for(int i=0;i<size;i++)
        first_[i]=rhs.first_[i];
    last_=first_+size;
    end_=first_+size;
}

测试代码

#include"vector.h"
void test()
{
    vector<int> vec;
    for(int i=0;i<20;i++)
    {
        vec.push_back(i);
        std::cout<<">>>"<<vec.back()<<std::endl;
        std::cout<<"size="<<vec.size()<<std::endl;
        std::cout<<"capacity="<<vec.capacity()<<std::endl;
    }
    while(!vec.empty())
    {
        std::cout<<vec.back()<<std::endl;
        vec.pop_back();
    }
}

int main()
{
    test();
    return 0;
}

使用空间配置器

为说明问题,我们使用上述实现的简易vector写一段测试代码

#include"vector.h"

class Test{
public:
    Test(){
        std::cout<<"Test"<<std::endl;
    }

    ~Test()
    {
        std::cout<<"~Test()"<<std::endl;
    }
};


void test()
{
    vector<Test> vec;
}

int main()
{
    test();
    return 0;
}

编译运行输出

pcl@robot:~/projects/myPro/vector$ g++ ./vector.cc -o main -g
pcl@robot:~/projects/myPro/vector$ ./main
Test
Test
Test
Test
Test
Test
Test
Test
Test
Test
~Test()
~Test()
~Test()
~Test()
~Test()
~Test()
~Test()
~Test()
~Test()
~Test()
pcl@robot:~/projects/myPro/vector$

可以看到,我只是在测试代码里单纯的定义了一个Test自定义类型的vector而已,还什么都没有做,就无缘无故的创建了10个Test对象

其原因在于,在vector的构造函数里,内存的创建与对象的构造是一块完成的

观察构造函数的实现,我们发现first数组数据成员是用new运算符完成的,而new运算符在分配内存的同时会调用对象的构造函数

template<typename T>
vector<T>::vector(size_t size)
:first_(new T[size])
,last_(first_)
,end_(first_+size)
{}

同理,我们观察vector的析构函数可以看到,vector析构函数里释放first数组时使用的是delete运算符,而delete运算符在释放内存的同时也会调用对象的析构函数 

template<typename T>
vector<T>::~vector()
{
    delete []first_;
}

因此,现在我们明白,我们的需求是

  • 将对象内存的分配与对象的构造分开
  • 将对象内存的释放与对象的析构分开

c++语言里有一个专门的空间配置器可以完成这一任务, 这也是一个类模版,其中

基本功能:

  1. 内存分配:分配足够的内存以存储特定数量的对象。
  2. 内存释放:释放之前分配的内存。
  3. 构造和销毁对象:在分配的内存中构造和销毁对象。

自定义配置器:

C++ 允许用户自定义配置器,以满足特殊的内存管理需求。自定义配置器需要实现以下基本方法:

  • allocate(): 分配内存。
  • deallocate(): 释放内存。
  • construct(): 在分配的内存中构造对象(在 C++11 后,这通常由容器自动管理)。
  • destroy(): 销毁对象(同样,C++11 后容器自动管理)。

事实上,这个空间配置器的实现原理是通过malloc和free函数实现的,因为malloc函数只进行内存的分配,而不会调用对象的构造函数,同理free函数也只进行内存的释放而不调用对象的析构

接下来,我们使用c++库为我们提供的空间配置器allocator改造我们的vector代码

源代码

#include<iostream>
#include<stdexcept>

template<typename T,typename Alloc=std::allocator<T>>
class vector{
public:
    vector(size_t size=10,Alloc allocator=std::allocator<T>());
    vector(const vector& rhs);
    vector<T,Alloc>& operator=(vector& rhs);
    ~vector();

    bool full() const;
    bool empty() const;
    size_t size() const;
    size_t capacity() const;
    
    void push_back(T& elm);
    void pop_back();
    T back();
private:
    T* first_;
    T* last_;
    T* end_;

    Alloc allocator_;
    void expand();
    void copy_from(const vector& rhs);
    void destroy_vector();
};

template<typename T,typename Alloc>
vector<T,Alloc>::vector(size_t size,Alloc allocator)
// :first_(new T[size])
/*只进行内存分配而不进行对象构造*/
:first_(allocator_.allocate(size))
,last_(first_)
,end_(first_+size)
,allocator_(allocator)
{}

template<typename T,typename Alloc>
vector<T,Alloc>::vector(const vector& rhs)
{
    copy_from(rhs);
}

template<typename T,typename Alloc>
vector<T,Alloc>& vector<T,Alloc>::operator=(vector& rhs)
{
    if(this!=&rhs)
    {
        // delete []first_;
        destroy_vector();
        copy_from(rhs);
    }
    return *this;
}

template<typename T,typename Alloc>
vector<T,Alloc>::~vector()
{
    // delete []first_;
    /*首先析构掉有效对象,再释放内存*/
    destroy_vector();
}

template<typename T,typename Alloc>
bool vector<T,Alloc>::full() const
{
    return last_==end_;
}

template<typename T,typename Alloc>
bool vector<T,Alloc>::empty() const
{
    return last_==first_;
}

template<typename T,typename Alloc>
size_t vector<T,Alloc>::size() const
{
    return last_-first_;
}

template<typename T,typename Alloc>
size_t vector<T,Alloc>::capacity() const{
    return end_-first_;
}

template<typename T,typename Alloc>
void vector<T,Alloc>::push_back(T& elm)
{
    if(full())
        expand();
    *last_++=elm;
}

template<typename T,typename Alloc>
void vector<T,Alloc>::pop_back()
{
    if(empty())
        throw std::out_of_range("vector is empty!");
    --last_;
}

template<typename T,typename Alloc>
T vector<T,Alloc>::back()
{
    if(empty())
        throw std::out_of_range("vector is empty!");
    return *(last_-1);
}

template<typename T,typename Alloc>
void vector<T,Alloc>::expand()
{
    size_t old_size=last_-first_;
    size_t new_size=old_size*2;
    // T* first=new T[new_size];
    T* first=allocator_.allocate(new_size);
    for(int i=0;i<old_size;i++)
    {
        // first[i]=first_[i];
        allocator_.construct(first+i,first_[i]);
    }
    // delete []first_;
    destroy_vector();
    first_=first;
    last_=first_+old_size;
    end_=first+new_size;
}

template<typename T,typename Alloc>
void vector<T,Alloc>::copy_from(const vector& rhs)
{
    size_t size=rhs.last_-rhs.first_;
    // first_=new T[size];
    /*只进行内存的分配*/
    first_=allocator_.allocate(size);
    for(int i=0;i<size;i++)
    {
        // first_[i]=rhs.first_[i];
        /*进行对象的构造,第一个参数的内存地址,第二个参数为内存里要构造的值*/
        allocator_.construct(first_+i,rhs.first_[i]);
    }
    last_=first_+size;
    end_=first_+size;
}

template<typename T,typename Alloc>
void vector<T,Alloc>::destroy_vector()
{
    for(T* p=first_;p!=last_;p++)
    {
        allocator_.destroy(p);
    }
    allocator_.deallocate(first_,size());
    first_=last_=end_=nullptr;
}

测试代码

#include"vector.h"

class Test{
public:
    Test(){
        std::cout<<"Test"<<std::endl;
    }

    ~Test()
    {
        std::cout<<"~Test()"<<std::endl;
    }
};


void test()
{
    std::cout<<">>>>"<<std::endl;
    vector<Test> vec;
    Test test;
    std::cout<<"====="<<std::endl;
    vec.push_back(test);
}

int main()
{
    test();
    std::cout<<"-----"<<std::endl;
    return 0;
}

再次编译运行查看结果

pcl@robot:~/projects/myPro/vector$ g++ ./vector.cc -o main -g
pcl@robot:~/projects/myPro/vector$ ./main
>>>>
Test
=====
~Test()
~Test()
-----
pcl@robot:~/projects/myPro/vector$

可以看到,除了测试代码中本身定义Test对象时调用了Test的构造函数以外,再没有多余的构造函数,同样也再没有多余的析构函数

自定义空间配置器

#ifndef MY_ALLOCTOR
#define MY_ALLOCTOR
#include <cstdlib>
template <typename T>
class Alloctor {
 public:
    T *allocate(int size) { return (T *)malloc(size * sizeof(T)); }

    void deallocte(T *ptr) { free(ptr); }

    void construct(T *ptr, const T &val) { new (ptr) T(val); }

    void destroy(T *ptr) { ptr->~T(); }
};

#endif

MyVector使用自定义空间配置器

#ifndef MY_VECTOR
#define MY_VECTOR
#include "myallocator.h"
#include <stdexcept>
template <typename T, typename Alloc = Alloctor<T>>
class MyVector {
 public:
    MyVector(int size = 5)
        // : first_(new T[size]), last_(first_), end_(first_ + size){}
        : first_(allocator_.allocate(size)), last_(first_),
          end_(first_ + size) {}

    MyVector(const MyVector &rhs) {
        int size = rhs.end_ - rhs.first_;
        int len = rhs.last_ - rhs.first_;
        // first_ = new T[size];
        first_ = allocator_.allocate(size);
        for (int i = 0; i < len; ++i) {
            // first_[i] = rhs.first_[i];
            allocator_.construct(first_ + i, rhs.first_[i]);
        }
        last_ = first_ + len;
        end_ = first_ + size;
    }

    MyVector &operator=(const MyVector &rhs) {
        if (this != &rhs) {
            // delete[] first_;
            for (int i = 0; i < (last_ - first_); ++i) {
                allocator_.destroy(first_ + i);
            }
            allocator_.deallocte(first_);

            int size = rhs.end_ - rhs.first_;
            int len = rhs.last_ - rhs.first_;
            for (int i = 0; i < len; ++i) {
                // first_[i] = rhs.first_[i];
                allocator_.construct(first_ + i, rhs.first_[i]);
            }
            last_ = first_ + len;
            end_ = first_ + size;
        }
        return *this;
    }

    T operator[](int &index) const {
        if (index < 0 || index >= (end_ - first_)) {
            throw std::out_of_range("Index out of range");
        }
        return *(first_ + index);
    }

    ~MyVector() {
        // delete[] first_;
        for (int i = 0; i < (last_ - first_); ++i) {
            allocator_.destroy(first_ + i);
        }
        allocator_.deallocte(first_);
        first_ = last_ = end_ = nullptr;
    };

    bool empty() const { return end_ == first_; };
    bool full() const { return last_ == end_; }
    int size() const { return last_ - first_; }
    int capacity() const { return end_ - first_; };

    T front() const { return *first_; }
    T back() const { return *(last_ - 1); }
    void push_back(const T &val) {
        if (full())
            expand();
        *(last_++) = val;
    };
    void pop_back() {
        if (empty())
            throw std::out_of_range("vector is empty!");
        last_--;
    };

 private:
    T *first_;
    T *last_;
    T *end_;
    Alloc allocator_;
    void expand() {
        int old_size = end_ - first_;
        int size = 2 * old_size;
        // T *tmp = new T[size];
        T *tmp = allocator_.allocate(size);

        for (int i = 0; i < old_size; ++i) {
            // tmp[i] = first_[i];
            allocator_.construct(tmp + i, first_[i]);
            allocator_.destroy(first_ + i);
        }
        // delete[] first_;
        allocator_.deallocte(first_);
        first_ = tmp;
        last_ = first_ + old_size;
        end_ = first_ + size;
        tmp = nullptr;
    }
};
#endif

测试代码

#ifndef LIB_TEST
#define LIB_TEST
#include <iostream>
class Test {
 public:
    Test();
    ~Test();
};
#endif
#include "lib_test.h"
Test::Test() { std::cout << "Test()" << std::endl; }
Test::~Test() { std::cout << "~Test()" << std::endl; }

 

#include "lib_test.h"
#include "tinyvector.h"
#include <iostream>

template <typename T>
std::ostream &operator<<(std::ostream &os, MyVector<T> &vec) {
    for (int i = 0; i < vec.size(); ++i) {
        os << vec[i] << " ";
    }
    os << std::endl;
    return os;
}

void test() {
    const int size = 8;
    MyVector<int> v;
    std::cout << "size=" << v.size() << std::endl;
    std::cout << "capacity=" << v.capacity() << std::endl;
    for (int i = 0; i < size; i++) {
        v.push_back(i + 1);
    }
    std::cout << "size=" << v.size() << std::endl;
    std::cout << "capacity=" << v.capacity() << std::endl;
    std::cout << v << std::endl;
    std::cout << "-----------------" << std::endl;
    MyVector<Test> vec;
    vec.push_back(Test());
}

int main() {
    test();
    return 0;
}
size=0
capacity=5
size=8
capacity=10
1 2 3 4 5 6 7 8 

-----------------
Test()
~Test()
~Test()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值