C++:模板实现vector和list

模板

模板分为模板函数和模板类。
模板是为了实现泛型编程,所谓泛型编程,就是指编写与类型无关的代码

模板函数

比如以下的场景:
我们想在同一份代码里实现交换整型,浮点型与字符型。

void Swap(int& x, int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
void Swap(double& x, double& y)
{
    double tmp = x;
    x = y;
    y = tmp;
}
void Swap(char& x, char& y)
{
    char tmp = x;
    x = y;
    y = tmp;
}

此时,就意味着我得写三份代码,如何我要交换更多类型的数据呢?
所以,模板的好处就在此。

实现一个交换的模板函数:

template <class T>
void Swap(T& x,T& y)
{
    T tmp = x;
    x = y;
    y = tmp;
}

但是,在实现了模板之后,编译器是如何识别的?实际上,模板函数并没有什么实际的意义,而是由编译器根据模板函数推演出函数的实例化,编译器才能够去调用该函数。

这里写图片描述

也就说是,我将参数传递给函数,编译器会根据该类型去推演处一份代码,然后去调用该函数。

再去汇编中看看:
这里写图片描述

显然这是两个不同的地址,就说明这确实是两个不同的函数,那么也就是说编译器去调用该代码时,推演出了两份实例化的代码。

实现vector

template <class T>
class Vector
{
protected:
    void Expand(size_t newcapacity)
    {
        if (newcapacity > Capacity())
        {
            T* tmp = new T[newcapacity];
            size_t size = Size();
            if (_first)
            {
                memcpy(tmp, _first, sizeof(T)*Size());
        /*  for (size_t i = 0; i < Size(); i++)
                {
                    tmp[i] = _first[i];
                }*/
                delete[] _first;
            }
            _first = tmp;
            _finish = _first + size;
            _endofstorage = _first + newcapacity;
        }
    }
public:
    //默认成员函数
    Vector()
        : _first(NULL)
        , _finish(NULL)
        , _endofstorage(NULL)
    {}
    //v1=v2;
    Vector(const Vector<T>& v)
    {
        _first = new T[v.size()];
        _finish = _first + v.Size();
        _endofstorage = _first + v.Size();
        memcpy(tmp, _first, sizeof(T)*Size());

    /*  for (int i = 0; i < v.Size(); i++)
        {
            _first[i] = v._first[i];
        }*/
    }
    Vector<T>& operator=(const Vector<int>& v)
    {
        swap(_first, v._first);
        swap(_finish, v._finish);
        swap(_endofstorage, v._endofstorage);
        return *this;
    }


    ~Vector()
    {
        if (_first)
        {
            delete[] _first;
        }
        _first = _finish = _endofstorage;
    }

    void PushBack(const T x)
    {
        Insert(Size(), x);
    }
    void PopBack()
    {
        Erase(Size());
    }


    void Insert(size_t pos, const T& x)
    {
        //assert(pos <= Size());
        if (_finish == _endofstorage)
        {
            size_t newcapacity = Capacity() == 0 ? 3 : Capacity() * 2;
            Expand(newcapacity);
        }
        //搬运
        T* end = _finish - 1;
        while (end >= _first + pos)
        {
            *(end + 1) = *end;
            end--;
        }
        _first[pos] = x;
        ++_finish;
    }
    void Erase(size_t pos)
    {
        assert(pos <= Size());

        T* start = _first + pos;
        while (start < _finish - 1)
        {
            *start = *(start + 1);
            start++;
        }
        --_finish;
    }
    size_t Size() const
    {
        return _finish - _first;
    }
    size_t Capacity()
    {
        return _finish - _first;
    }
    bool Empty()
    {
        return _finish == _first;
    }
    T& operator[](size_t pos)
    {
        assert(pos < Size());
        return _first[pos];
    }
    void Resever(size_t size);

    void Resize(size_t size, const T& value = T());

protected:

    T* _first;
    T* _finish;
    T* _endofstorage;

};


实际上这个代码中有一个细节必须注意,在进行拷贝值时,会牵扯到深浅拷贝的问题。

这里写图片描述
比如上面这段代码,在vs2013下是一定会崩掉的,原因是取决于编译器对string的实现不同。

在调试窗口观察string:
可以看到string里除了ptr还有一个16字节大小的buf,“aaa”就存在于buf中。
这里写图片描述

当扩容拷贝值时:
这里写图片描述

如果是上面那份代码的情况:
这里写图片描述

可以看到,超长的字符串buf已经村放不下了,那么就会在内存中开辟空间来存放,ptr指向这段内存空间,拷贝时,将ptr也拷贝一份,就会出现如下情况:

这里写图片描述

这样看似好像也没有问题,但是释放空间就会出问题:
这里写图片描述

实现List

using namespace std;
template <class T>
struct ListNode
{
    ListNode<T>* _prev;
    ListNode<T>* _next;
    T _data;

    ListNode(const T& x)
        :_data(x)
         ,_prev(NULL)
         ,_next(NULL)
    {}
};
template <class T>
class List
{
    typedef ListNode<T> Node;
public:
    List()
    {
        _head=new Node(T());
        _head->_prev=_head;
        _head->_next=_head;
    }

    ~List()
    {

        Node* cur=_head->_next;
        while(cur!=_head)
        {
            _head->_next=cur->_next;
            delete cur;
            cur=_head->_next;
        }
        delete _head;
        _head=NULL;
    }

    List(const List<T>& l)
        :_head(new Node(T()))
    {
        Node* cur=l._head->_next;
        while(cur!=_head)
        {
            PushBack(cur->_data);
            cur=cur->_next;
        }
    }

    List<T>& operator=(const List<T>& l)
    {
        swap(_head,l._head);
        return *this;
    }

    void PushBack(const T x)
    {
        Insert(_head,x);
    }
    void PopBack()
    {
        Erase(_head->_prev);
    }
    void PushFront(const T x)
    {
        Insert(_head->_next,x);
    }
    void PopFront()
    {
        Erase(_head->_next);
    }
    //prev pos
    //prev newnode next
    void Insert(Node* pos,T x)
    {
        assert(pos);
        Node* new_node=new Node(x);
        Node* prev=pos->_prev;
        Node* next=pos;

        new_node->_next=next;
        next->_prev=new_node;
        new_node->_prev=prev;
        prev->_next=new_node;
    }
    //prev pos next
    void Erase(Node* pos)
    {
        assert(pos&&_head->_next!=_head);

        Node* next=pos->_next;
        Node* prev=pos->_prev;

        prev->_next=next;
        next->_prev=prev;

        delete pos;
    }

    size_t Size()
    {
        Node* cur=_head->_next;
        size_t size=0;
        while(cur!=_head)
        {
            size++;
            cur=cur->_next;
        }
    }
    bool Empty()
    {
        return _head->_next==_head;
    }
    void ListPrint()
    {
        Node* cur=_head->_next;
        while(cur!=_head)
        {
            cout<<cur->_data<<" ";
            cur=cur->_next;
        }
        cout<<endl;
    }



private:
    Node* _head;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值