《深入实践C++模板编程》之五——容器与迭代器

1、容器的定义

容器:专门用于某种形式组织及存储数据的类称为“容器”。

2、容器与迭代器

迭代器:封装了对容器虚拟数据序列的操作并按约定提供统一界面以遍历容器内容的代理类即为迭代器。

举例理解:

template<typename T> class list;

template<typename T>
struct list_node
{
    typedef T value_type;
    typedef T& reference_type;
    typedef const T const_reference_type;

    T value;
    list_node *prev;
    list_node *next;

    list_node(T const &value, list_node *prev, list_node *next) :
        value(value), prev(prev), next(next){}
};

template<typename N>
class list_iterator
{
    N *pos;
    template<typename T> friend class list;

public:
    typedef typename N::value_type value_type;
    typedef typename N::reference_type reference_type;
    typedef typename N::const_reference_type const_reference_type;
    typedef list_iterator<N> self_type;

    list_iterator() :pos(0){}
    list_iterator(N *pos) :pos(pos){}

    bool operator != (self_type const &right) const{
        return pos != right.pos;
    }

    bool operator == (self_type const &right) const{
        return pos != right.pos;
    }

    self_type& operator++(){
        if (pos) pos = pos->next;
        return *this;
    }

    reference_type operator * () throw (std::runtime_error){
        if (pos) return pos->value;
        else throw (std::runtime_error("null iterator!\n"));
    }
};

template<typename T>
class list
{
    typedef list_node<T> node_type;
    node_type *head;

public:
    typedef T value_type;
    typedef list_iterator<node_type> iterator;

    list() :head(){}
    ~list(){
        while (head)
        {
            node_type *n = head;
            head = head->next;
            delete n;
        }
    }

    void push_front(T const &v)
    {
        head = new node_type(v, 0, head);
        if (head->next)
        {
            head->next->prev = head;
        }
    }

    void pop_front(T const &v)
    {
        if (head)
        {
            node_type *n = head;
            head = head->next;
            head->prev = 0;
            delete n;
        }
    }

    void insert(iterator it, T const &v)
    {
        node_type *n = it.pos;
        if (n)
        {
            node_type *new_node = new node_type(v, n, n->next);
            new_node->next->prev = new_node;
            n->next = new_node;
        }
    }

    void erase(iterator &it)
    {
        node_type *n = it.pos;
        ++it;
        if (n)
        {
            if (n->next)
            {
                n->next->prev = n->prev;
            }
            if (n->prev)
            {
                n->prev->next = n->next;
            }
            if (head == n)
            {
                head = n->next;
            }
            delete n;
        }
    }

    bool is_empty() const { return head == 0; }
    iterator begin(){ return iterator(head); }
    iterator end(){ return iterator(); }
};

针对树形结构节点类型的迭代器:

template<typename T>
struct tree_node
{
    typedef T value_type;
    typedef T& reference_type;
    typedef const T& const_reference_type;

    T value;
    tree_node *parent;
    tree_node *left;
    tree_node *right;

    tree_node(T const &value,
        tree_node *parent,
        tree_node *left,
        tree_node *right):
        value(value),
            parent(parent),
            left(left),
            right(right){}

        ~tree_node()
        {
            if (left) delete left;
            if (right) delete right;
        }
};

template<typename N>
class tree_iterator
{
    const N *pos;
public:
    typedef typename N::value_type value_type;
    typedef typename N::const_reference_type const_reference_type;
    typedef tree_iterator<N> self_type;

    tree_iterator() :pos(0){}
    tree_iterator(const N *pos) :pos(pos){}

    bool operator == (self_type const &right) const{
        return pos == right.pos;
    }

    self_type& operator ++ (){
        if (pos){
            if (pos->right){
                pos = pos->right;
                while (pos->left)
                {
                    pos = pos->left;
                }
            }
            else
            {
                while (pos->parent && (pos->parent->right == pos))
                {
                    pos = pos->parent;
                }
                pos = pos->parent;
            }
        }
        return *this;
    }

    const_reference_type operator * () const throw(std::runtime_error)
    {
        if (pos)
        {
            return pos->value;
        }
        else
        {
            throw std::runtime_error("Null iterator!\n");
        }
    }
};

template<typename T>
class set
{
    typedef tree_node<T> node_type;
    node_type *root;
public:
    typedef T value_type;
    typedef tree_iterator<node_type> const_iterator;

    set() :root(){}
    ~set(){ if (root) delete root; }

    bool insert(T const &v)
    {
        node_type **n = &root;
        node_type *p = 0;
        while (*n)
        {
            if (v == (*n)->value)
            {
                return false;
            }
            else
            {
                p = *n;
                n = v < (*n)->value ? &((*n)->left) : &((*n)->right);
            }
        }
        *n = new node_type(v, p, 0, 0);
        return true;
    }

    bool has(T const &v)
    {
        node_type *n = root;
        while (n)
        {
            if (v == n->value)
                return true;
            n = v < n->value ? n->left : n->right;
        }
        return false;
    }

    bool is_empty() const { return root == 0; }

    const_iterator begin() const{
        node_type *n = root;
        while (n->left) n = n->left;
        return const_iterator(n);
    }

    const_iterator end() const { return const_iterator(); }
};

尽管容器内部的数据结构类型不同(set是一个树形结构,而list是链表结构),但是提供给用户的操作界面iterator的所有操作都是相同的,所以底层数据结构对用户来说是透明的。

这也算是一种设计思路。

3、迭代器和算法

利用迭代器求和:

template<typename C>
typename C::value_type
sum(C &c)
{
    typedef typename C::value_type value_type;
    typedef typename C::iterator iterator;
    value_type sum(0);

    for (iterator i = c.begin(); i != c.end(); ++i)
    {
        sum += *i;
    }
    return sum;
}

不足1:只能求所有元素之和,不能求部分元素之和。可以将一个迭代器改为两个迭代器来弥补这里的不足。

template<typename I>
typename I::value_type
sum(I begin, I end)
{
    typedef typename I::value_type value_type;

    value_type sum(0);
    for (; begin != end; ++begin)
    {
        sum += *begin;
    }
    return sum;
}

不足2:要兼容最原始的容器——数组。

这里的I都是某种类型的迭代器,你可以通过I::value_type获取迭代器所指的对象的类型。但是数组的指针是一个普通的指针,是没有value_type成员的。借助另外一个类模板及模板特例功能来统一描述迭代器指针的特性。

再进行一层封装,使得普通的指针也有了value_type:

template<typename I>
struct iterator_traits
{
    typedef typename I::value_type value_type;
};

template<typename P>
struct iterator_traits<P*>
{
    typedef P value_type;
};

改进后:

template<typename I>
typename iterator_traits<I>::value_type
sum(I begin, I end)
{
    typedef typename iterator_traits<I>::value_type value_type;

    value_type sum(0);
    for (; begin != end; ++begin)
    {
        sum += *begin;
    }
    return sum;
}

4、迭代器的陷阱

a、容器中通常有迭代器为参数对指定位置进行操作的成员函数,调用此类函数有个隐含要求,即迭代器所指数据来自该容器,否则后果无法预测。

b、由于迭代器独立于容器之外,用户可以一次性生成多个迭代器对容器内数据进行操作。这样就会产生冲突。

原文地址:https://www.cnblogs.com/predator-wang/p/11509831.html

时间: 2024-11-03 01:33:05

《深入实践C++模板编程》之五——容器与迭代器的相关文章

《深入实践C++模板编程》之六——标准库中的容器

1.容器的基本要求 a.并非所有的数据都可以放进容器当中.各种容器模板对所存数据类型都有一个基本要求——可复制构造.将数据放进容器的过程就是通过数据的复制构造函数在容器内创建数据的一个副本的过程. b.容器中必须有若干与所存数据类型有关的嵌套定义类型. C::value_type 容器所存数据类型 C::reference 容器数据的引用类型 C::const_reference 容器数据的只读引用类型 C::size_type 容器容量类型,通常是一个无符号整数类型 c.除嵌套类型定义外,容器

C++笔记(6):标准模板库STL:容器、迭代器和算法

STL(Standard Template Library)是C++标准库的一部分.STL的代码从广义上讲分为三类:容器.迭代器和算法. 1.容器 2.迭代器 3.算法  -------------------------------------------------------------------------------------------------------------------------- 1.容器 顺序容器容器是特定类型对象的集合.顺序容器为程序员提供控制元素存储和访问

[C++]C++的模板编程

我是搬运工,原文地址:http://www.cppblog.com/besterChen/archive/2010/07/22/121000.html 当我们越来越多的使用C++的特性, 将越来越多的问题和事物抽象成对象时, 我们不难发现:很多对象都具有共性. 比如 数值可以增加.减少:字符串也可以增加减少. 它们的动作是相似的, 只是对象的类型不同而已. C++ 提供了“模板”这一特性, 可以将“类型” 参数化, 使得编写的代码更具有通用性. 因此大家都称模板编程为 “通用编程”或 “泛型编程

C++的模板编程

当我们越来越多的使用C++的特性, 将越来越多的问题和事物抽象成对象时, 我们不难发现:很多对象都具有共性. 比如 数值可以增加.减少:字符串也可以增加减少. 它们的动作是相似的, 只是对象的类型不同而已. C++ 提供了“模板”这一特性, 可以将“类型” 参数化, 使得编写的代码更具有通用性. 因此大家都称模板编程为 “通用编程”或 “泛型编程”. 一般而言, 模板分为 函数模板 和 类模板,下面就让我们分别来了解一下它们. 一. 函数模板 1. 函数模板的定义和使用 定义一个模板函数的格式并

STL(标准模板库)理论基础,容器,迭代器,算法

基本概念 STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称.现然主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间. STL的从广义上讲分为三类:algorithm(算法).container(容器)和iterator(迭代器),容器和算法通过迭代器可以进行无缝地连接.几乎所有的代码都采 用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会.在C++标准中,STL被组织为下面的13

[推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼、百战不殆)

原文:[推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼.百战不殆) [推荐]ORACLE PL/SQL编程之五: 异常错误处理(知已知彼.百战不殆) 继上三篇:ORACLE PL/SQL编程之八:把触发器说透 ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!) [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) 得到了大家的强力支持与建议,万分感谢.接下来介绍下一篇:oracle pl/sql异常处理部分,还望大家一定

c++ 模板参数做容器参数,迭代器报错 vector&lt;T&gt;::const_iterator

错误如下: template<class T>void temp(std::vector<T>& container){        std::vector<T>::const_iterator p; //error: expected ‘;’ before ‘p’        for(p = container.begin(); p != container.end(); ++p)        {                //...       

C++ 11可变参数接口设计在模板编程中应用的一点点总结

概述 本人对模板编程的应用并非很深,若要用一句话总结我个人对模板编程的理解,我想说的是:模板编程是对类定义的弱化. 如何理解“类定义的弱化”? 一个完整的类有如下几部分组成: 类的名称: 类的成员变量(或属性,C#中属性和成员变量还是有区别的): 类的成员方法: 从编译器的角度看,我们必须明确指定以上3部分,才算完整地定义了一个类并且编译通过. 所谓的“类弱化”,是指类的设计者在定义类的时候,并没有完整定义一个类,而是把类的其中一部分的定义留给类的使用者. 从传统才c++98看,通过模板类,使用

【转】[推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼、百战不殆)

[推荐]ORACLE PL/SQL编程之五: 异常错误处理(知已知彼.百战不殆) 继上三篇:ORACLE PL/SQL编程之八:把触发器说透 ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!) [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) 得到了大家的强力支持与建议,万分感谢.接下来介绍下一篇:oracle pl/sql异常处理部分,还望大家一定要支持与推荐呀~! 本篇主要内容如下: 5.1 异常处理概念 5.1.1 预定义的异常