与我一起学C++之list<二>

来源:互联网 发布:java抓取淘宝商品信息 编辑:程序博客网 时间:2024/05/17 10:05

上一章我们讲解了list_nodelist_iterator,这一章我们主要来讲讲list 本身的实现。

废话不说,直接看代码

1、 构造函数和成员变量

class list{public:    list();private:    list_node*  head_;    list_node*  tail_; };list::list(){    head_ = new list_node;    tail_ = new list_node;    head_->next = tail_;    head_->prev = nullptr;    tail_->prev = head_;    tail_->next = nullptr;}

首先我们在list内部封装了两个list_node的指针,作为前哨兵和尾哨兵,并且在构造的时候,给这两个指针申请内存并互相指向。

两个哨兵模式的某个list实例的内存模型可以用下图表示

这里写图片描述

在STL中都是用的一个哨兵的模式,感兴趣的同学可以自行画图表示一下,它实际上是一个环状的链式结构。我们这里用两个哨兵的目的是为了让语义更清楚而已。

2、list是否为空

从上面的图很容易看出来,当head_->next指针直接指向tail_指针时,该list就为空

bool list::empty() const{    return head_->next == tail_;}

3、begin 和 end

同样从上图看,很显然我们可以写出

list_iterator list::begin() { return list_iterator(head_->next); }list_iterator list::end() { return list_iterator(tail_); }

4、 list的大小

有了begin()end() 迭代器,很容易就可以得到list的大小了。

int list::size() const{    int size_ = 0;    distance(begin(), end(), size_);    return size_;}void list::distance(list_iterator first,list_iterator last,int& result) const{    for (auto it = first; it != last; ++it)        ++result;}

如果您看过《Effective STL》,那您应该记得有一款条目是说,永远不要用if(size() == 0)而是empty() 来判断一个容器是否为空。从这您就可以端倪来了,是的,对于部分容器来说,调用一次size()的时间复杂度为O(n),而调用一次empty()的时间复杂度为O(1),孰优孰劣可想而知了。

条款 4 : 用empty来代替检查size是否为0 (Effective STL 中文版 25页)

5、front 和 back

与上面类似,通过begin 和 end 很快就可以得到 front 和 back的值

int& list::front() const { return *begin();}int& list::back() const { return *(--end());}

6、插入和添加元素

6.1 插入元素

在添加元素之前,我们先来考虑怎么插入一个元素。首先,在某个节点处插入一个元素,我们还需要考虑是作为前驱(as prev)还是作为后继(as next)插入。通过查看gnu gcc 提供的源代码,STL中是作为前驱来插入的。

在写代码之前,我们可以通过画图等辅助方式来帮助我们分析,毕竟有图才有真相么…

这里写图片描述

据图可以写出如下代码

private:list_iterator list::insertAsPerv(list_iterator position,const int &task){    list_iterator tmp(new list_node);    *tmp = task;    position.node_->prev->next = tmp.node_;    tmp.node_->prev = position.node_->prev;    tmp.node_->next = position.node_;    position.node_->prev = tmp.node_;    return tmp;}public:list_iterator list::insert(list_iterator position,const int &task){    return insertAsPerv(position,task);}

6.2 在前部和后部添加元素

当插入一个元素时,我们是作为前驱插入的,所以对于push_backpush_front我们可以像如下编写

void list::push_back(const int &value){ insert(end(), value); }void list::push_front(const int &value){ insert(begin(), value); }

7、查找某个元素 find

一般我们用find() 时,通常意味着是在一个无序的数据结构(容器)中查找,对于list中的查找,我们只能遍历所有元素才进行。

list_iterator list::find(list_iterator first, list_iterator last, const int &task){    list_iterator iter = first;    while (iter != last)    {        if (*iter == task)            return iter;        ++iter;    }    return last;}

8、测试

写了一些接口之后,我们来写个小demo测试一下我们的程序是否有错误。

#include "list.h"#include <iostream>#include <cstdlib>int main(){    std::cout<<"-------------- list test -----------------"<<std::endl<<std::endl;    yang::list mylist;    if(mylist.empty())        std::cout<<"this is the empty list !"<<std::endl<<std::endl;    for(int i = 0;i < 5; ++i)        mylist.push_back(i);    auto it = mylist.begin();    std::cout<<"elements are :   ";    for(;it != mylist.end(); ++it)        std::cout<<*it<<std::ends;    std::cout<<std::endl<<std::endl;    mylist.push_front(5);    if(!mylist.empty())        std::cout<<"this is not the empty list !"<<std::endl<<std::endl;    std::cout<<"size is "<<mylist.size()<<std::endl<<std::endl;    std::cout<<"front is "<<mylist.front()<<std::endl<<std::endl;    std::cout<<"back is "<<mylist.back()<<std::endl<<std::endl;    it = mylist.begin();    ++it;    mylist.insert(it,-99);    std::cout<<"elements are :   ";    for(it = mylist.begin();it != mylist.end(); ++it)        std::cout<<*it<<std::ends;    std::cout<<std::endl<<std::endl;    if(mylist.find(mylist.begin(),mylist.end(),-99) != mylist.end())        std::cout<<"find the task -99 ! ..." <<std::endl<<std::endl;    if(mylist.find(mylist.begin(),mylist.end(),100) == mylist.end())        std::cout<<"can not find the task 100 ! ..." <<std::endl<<std::endl;    system("pause");    return 0;}// 以上代码均在 win7下,visual studio 2013 测试完成

测试结果如下:

这里写图片描述

从结果来看,我们写的list暂时还是没有错误的,可喜可贺:)无耻….

总结:本章主要讲了list底层的结构和一些最基本的接口函数,限于篇幅原因,还有一些接口的实现如sort,remove,erase等等我们留到下一章再讲解。

参考资料
STL 源码解析 侯捷著
数据结构(C++语言版)第三版 邓俊辉著
gcc 2.95 version source code

0 0
原创粉丝点击