初识STL 剖析list部分源码

1、STL

   库函数的设计第一位是通用性,模板为其提供了可能;标准模板库中的所有算法和容器都是通过模板实现的。

 STL(标准模板库)是 C++最有特色,最实用的部分之一。

STL整个架构模型如下:

2、list(双向循环链表)

调用STL系统的#include<list>,用系统的双向循环链表结构处理:

#include<iostream>
#include<list> //调用系统的list,双向循环链表结构
using namespace std;

int main(void){
     
    list<int> mylist;
    for(int i = 1; i <= 10; i++){
        mylist.push_back(i);  //接口,末尾增加
    }
    list<int>::iterator it = mylist.begin(); //迭代器,
    while(it != mylist.end()){
        cout<<*it<<"-->"; //打印内部数字
        ++it;   //每次往后移一个
    }
    cout<<"NULL"<<endl;
}

3、list部分源码实现剖析

list模型如下:

阅读其源代码,分析了部分的功能:

#ifndef _LIST_H   //条件宏编译,避免重复定义
#define _LIST_H

#include<assert.h>   //断言引入的头文件
#include<malloc.h>   //申请空间所引入的头文件

template<class _Ty> //此处先不涉及空间置配器
class list{    //list类
public:
    struct _Node;
    typedef struct _Node* _Nodeptr;  //指向节点的指针类型
    struct _Node{   //_Node这个是节点类型
        _Nodeptr _Prev;    //前驱节点
        _Nodeptr _Next;    //后继节点
        _Ty      _Value;   //模板数据类型
    };
    struct _Acc{  //定义_Acc这个类型
        typedef struct _Node*& _Nodepref;  //指向节点类型指针的引用
        typedef _Ty&           _Vref;      //这个数据类型的引用
        static _Nodepref _Next(_Nodeptr _P)//静态方法, 返回值是节点指针的引用 ,参数是指向节点的指针
        {return ((_Nodepref)(*_P)._Next);}//:*_P得到这个节点,()强制类型转换的优先级没有.高,所以此时先取_Next,在进行强制类型转换的工作,返回一个指向节点指针的引用。
        static _Nodepref _Prev(_Nodeptr _P)
        {return ((_Nodepref)(*_P)._Prev);}
        static _Vref _Value(_Nodeptr _P)
        {return ((_Vref)(*_P)._Value);} 
    };
public:  //以下的类型是_A这个类下面的类型,_A这个类在空间置配器中定义
    typedef typename _A::value_type           value_type;
    typedef typename _A::pointer_type         pointer_type;
    typedef typename _A::const_pointer_type   const_pointer_type;
    typedef typename _A::reference_type       reference_type;
    typedef typename _A::const_reference_type const_reference_type;
    typedef typename _A::size_type            size_type;  //这个类型其实就是size_t

private:
    _Nodeptr  _Head;   //指向头结点的指针
    size_type _Size;   //有几个节点个数
};

#endif

以上代码主要是struct  _Acc这个类的理解好至关重要!!!

下面就是构造函数和析构函数了

public:
    explicit list():_Head(_Buynode()),_Size(0)  //explicit显示调用此构造函数,给头一个指向,刚开始0个
    {}
    ~list()
    {     //释放空间和空间配置器有关,在现阶段先不关心。
        erase(begin(), end());  //调用开始,结束函数释放空间;
        _Freenode(_Head);       //释放头;
        _Head = 0, _Size = 0;   //都赋空;
    }
    ..................................................
protected:
    _Nodeptr _Buynode(_Nodeptr _Narg=0, _Nodeptr _Parg=0)  // 返回值为节点指针类型,参数都为节点指针类型,传的应该是后继和前驱指针,默认都为0;
    {
        _Nodeptr _S = (_Nodeptr)malloc(sizeof(_Node));//申请一个节点空间,把地址给了_S;
        
        assert(_S != NULL);  //所申请的空间存在的话
        _Acc::_Next(_S) = _Narg!=0 ? _Narg : _S; //给新生成的节点的_Next赋值
        _Acc::_Prev(_S) = _Parg!=0 ? _Parg : _S; //给新生成的节点的_Prev赋值
        return _S; //返回这个新生成节点的地址
    }
//这个_Buynode函数的意思是:当创建的是第一个节点时,自己一个节点连向自己,构成双向循环链表,其他的情况则是插入到两个节点之间!!!
........................................................

接下来写迭代器:

public:
    class iterator{   //迭代器也是一个类,是list的内部类;
    public:
        iterator()
        {}
        iterator(_Nodeptr _P):_Ptr(_P)
        {}
    public:
        iterator& operator++(){  // ++it,前++的运算符重载
            _Ptr=_Ptr->_Next; //因为是链表结构,内部实现迭代器的++,是进行了++的重载;使其指针的移动到下一个节点;
            return *this;   //返回的是这个节点的引用。
        }
        iterator operator++(int)// it++
        {
            _It it(_Ptr);  //先保存原先节点
            _Ptr = _Ptr->_Next; //移到下一个节点
            return it;  //返回原先的;
        }
        iterator operator--(int); //类似
        iterator& operator--();
        reference_type operator*()const //对*的重载
        {return _Ptr->_Value;}   //返回这个节点的_Value值
        pointer_type operator->()const //对->的重载
        //{return &_Ptr->_Value;}  自己实现的,->的优先级高于&,所以将_Value的地址返回
        {return (&**this);}  //系统中的,this是迭代器的地址,*this是迭代器对象,再来一个*时,调用上面的(对*的重载),此时还是返回_Value的地址。
    public:
        bool operator!=(const iterator &it)const  //迭代器对象的比较
        {return _Ptr!=it._Ptr;}  //比的是指向节点的指针;
    public:
        _Nodeptr _Mynode()const //得到当前节点的地址;
        {return _Ptr;}
    protected:
        _Nodeptr _Ptr;   //迭代器的数据成员是一个指向节点的指针。
    };
    typedef iterator _It;  //_It 就是迭代器类型
public:
    iterator begin(){return iterator(_Acc::_Next(_Head));}  //begin()函数得到头结点的后继(第一个有效节点的地址)
    iterator begin()const;
    iterator end(){return iterator(_Head);}  //end()函数得到的是头结点(也就是最后一个节点的后继地址);
public:                        //前面的已经讲的很清楚了,后面的都是调用即可;
    void push_back(const _Ty &x)  
    {insert(end(),x);}
    void push_front(const _Ty &x)
    {insert(begin(),x);}
public:
    iterator insert(iterator _P, const _Ty &_X=_Ty())
    {
        _Nodeptr _S = _P._Mynode();  //得到节点地址
        _Acc::_Prev(_S) = _Buynode(_S, _Acc::_Prev(_S));  //下面的三句调用前面的函数_Buynode()实现了插入功能;
        _S = _Acc::_Prev(_S);
        _Acc::_Next(_Acc::_Prev(_S)) = _S;
        ++_Size;  //个数加1
        return iterator(_S);
    }
    void insert(iterator _P, size_type _M, const _Ty &_X) //插入个数_M个,以下几个调用前面函数;
    {
        for(; 0<_M; --_M)
            insert(_P,_X);
    }
    void insert(iterator _P, const _Ty *_F, const _Ty *_L) //区间的插入
    {
        for(; _F!=_L; ++_F)
            insert(_P, *_F);
    }
    void insert(iterator _P, _It _F, _It _L)  //迭代器的插入
    {
        for(; _F!=_L; ++_F)
            insert(_P, *_F);
    }
    /*
    void push_back(const _Ty &x)  //尾随增加最后
    {
        _Nodeptr _S = _Buynode(_Head, _Acc::_Prev(_Head)); //实现插入功能
        _Acc::_Value(_S) = x;
        _Acc::_Next(_Acc::_Prev(_Head)) = _S;
        _Acc::_Prev(_Head) = _S;
        _Size++;  //最后加1
    }

    iterator erase(iterator _P)// 删除空间
    {
        _Nodeptr _S = (_P++)._Mynode();
        _Acc::_Next(_Acc::_Prev(_S)) = _Acc::_Next(_S);
        _Acc::_Prev(_Acc::_Next(_S)) = _Acc::_Prev(_S);
 
        --_Size;  //个数减少1个
        return _P;
    }
    iterator erase(iterator _F, iterator _L) //调用函数,删除区间
    {
        while(_F != _L)
            erase(_F++);
        return _F;
    }
    void clear() //清除所有空间
    {erase(begin(), end());}

#endif

4、总结:

   (1)、迭代器的本质有了了解,是一个内部类,它将是一个对象,内部数据成员是一个指向节点的指针;

        (2)、迭代器对->的重载返回的是节点内部数据的地址,而不是节点的地址;

        (3)、迭代器对每种数据结构的实现均不相同,(Stack, queue, list...........)

        (4)、空间配置器:对所有的数据结构而言,只有一份,

                            作用:申请,释放空间,构造,析构对象;


时间: 2024-10-13 09:51:34

初识STL 剖析list部分源码的相关文章

【Java集合源码剖析】HashMap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap. HashMap 实现了Serializable接口,因此它支持序列化,

转:【Java集合源码剖析】Vector源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35793865   Vector简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. Vector没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,能被克隆,实

【Java集合源码剖析】ArrayList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35568011 ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用concurrent并发包下的C

转:【Java集合源码剖析】HashMap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动

转:【Java集合源码剖析】Hashtable源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/36191279 Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中. Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆.

C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析,左值与右值

C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析 by 小威威 1. 知识引入 在C++编程中,动态分配的内存在使用完毕之后一般都要delete(释放),否则就会造成内存泄漏,导致不必要的后果.虽然大多数初学者都会有这样的意识,但是有些却不以为意.我曾问我的同学关于动态内存的分配与释放,他的回答是:"只要保证new和delete成对出现就行了.如果在构造函数中new(动态分配内存),那么在析构函数中delete(释放)就可以避免内存泄漏了!" 事实果真如此么?

转:【Java集合源码剖析】LinkedList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253   您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011 LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使

【Java集合源码剖析】Vector源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35793865 Vector简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. LinkedList是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. LinkedList没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,

转:【Java集合源码剖析】ArrayList源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/35568011   本篇博文参加了CSDN博文大赛,如果您觉得这篇博文不错,希望您能帮我投一票,谢谢! 投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011   ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayL