4 pointer-like classes 长得像指针的类 (智能指针&迭代器)

4 pointer-like classes

智能指针

一个 C++ 的 class, 它做出来可能会像两种东西, 很特别的两种东西, 我们分别来讨论它们.

一种就是, 这个 class 做出来之后所产生出来的对象, 这个对象像一个指针, 很特别, 所以我们叫它 pointer-like classes

为什么我们需要一个 class 像一个指针呢, 因为我们想要比指针再多做一些事情, 所以通常我们又把它叫作智能指针.

C++ 在 2.0 之前有一个智能指针 auto pointer, 现在 2.0 之后有好几个智能指针, 包括现在所看到的 shared_ptr

template<class T>
class shared_ptr
{
    public:
        T& operator*() const
        {   return  *px;    }

        T* operator->() const
        {   return px;  }

        shared_ptr(T* p) : px(p) { }

    private:
        T*  px;
        long*   pn;

 /*...*/
};

这些智能指针的长相都类似于这样, 里面一定会有一个真正的指针.

图里面大的圈圈就是智能指针, 里头一定会有一个真的正指针

我们现在不讨论这个 shared pointer 智能指针到底智能在哪里, 我们来讨论它的语法

代码里面有这么一个变量 px, 变量类型是 T 指针, 也就是图上绿色的部分, 它指向一个模板 T

我们这个 pointer-like class 要强调的是这个指针能够做的事情, 这个 class 的对象也要能做

什么样的操作符用在指针身上呢 ? 以现在这个 shared pointer 来讲有两个符号, 一个是星号一个是箭头, 因为这个智能指针要代表一般的指针, 一般的指针除了这两个动作之外还有其它的, 我们这里只关注这两个符号

这一类的智能指针, 星号和箭头的重载写法几乎是固定的

这两个做了什么事情呢 ? 我们以一个例子来讲解.

现在我有一个 class 叫 Foo, 我要把 Foo 的指针, 这个 new 就是指针

我要把这个指针包装到智能指针里面去

一般这种智能指针都会有这么一种类似于 shared_ptr(T* p) : px(p) { } 的构造函数, 接受 C++ 天然的指针

struct Foo
{
    /*...*/
    void method(void) { /*...*/ }
};

shared_ptr<Foo> sp(new Foo);
// new 一个 Foo 之后得到的指针当成初值, 也就是调用其构造函数, 塞进去, 于是这个指针就跑到上面智能指针构造函数的 p 那里了, 然后 p 赋值到 px 上面

// 于是就形成了我们图中的形态. 使用者面对这样一种形态, 使用者面对它就要把它当成像一般的指针一样

// 因此使用者可能像下面这样去使用它

// 这样也正好呼应了我们在智能指针代码里面的操作符重载
Foo f(*sp);
// 对使用者而言, sp 就代表原来那一根指针了, 星号 * 作用在上面就是要解引用, 就是要取出它所指向的那一个东西
// 现在它要去取出那一个东西, 我们在智能指针代码里要怎么解释这一个行为呢 ?, 这就对应了星号 * 的操作符重载.
// 对于这个智能指针的星号操作符重载, 无一例外都是这么写

sp->method();
// 使用者通过这个智能指针, 这个箭头符号, 意思是要调用这个 method 函数
// 指针指向一个对象, 通过这个对象调用这个 method 函数
// 作用效果等同于下面这行代码
// 因为箭头操作符返回的是 px 指针
// px->method(); 放在这里帮助理解

对于 pointer-like class, 最经典的就是智能指针, 它里面一定有一个一般的指针, 这个智能指针一定要重载星号和箭头符号, 并且重载的写法固定

迭代器

百度百科迭代器 : https://baike.baidu.com/item/%E8%BF%AD%E4%BB%A3%E5%99%A8/3803342

上面是第一大分类, 现在我们看第二大分类 - 迭代器

迭代器代表容器里面的一个元素, 它要去指向一个元素, 因此它也像一个指针, 你可以说它就是智能指针, 但是这个和上面的智能指针长得略有不同

在这里, 迭代器这种智能指针, 不光要处理星号 * 和箭头 ->, 还要处理好 ++ -- == != 这些其它东西

其它智能指针可能不用去处理 ++ -- 这些东西

对于指针来说, ++ -- 就是代表指针的移动, 迭代器主要就是用来遍历容器里面的东西的, 所以它需要写出 ++ --

这里是链表的迭代器, 我们把重载星号和箭头的函数代码拿出来


在标准库里面的容器, 链表的长相如图中下半部分所示

这里是一个双向链表, 所以每一个 data 上面带有两个指针

这里链表的迭代器画成图就是图上圈圈泡泡, 它里面必然有一个真正的指针 (第一张图绿色的 node 就是的)

回到第二张图, 作为一个使用者, 我在这个容器里面, 确认了这个泡泡指向这个节点之后, 我对这个泡泡做星号 * 操作, 这个星号 * 是什么意思 ?

链表的使用者不知道什么泡泡这种东西, 使用者心理所想的就是迭代器指向这么一个元素, 所以对迭代器做星号 * 解引用, 其实它的意思就是取出这个元素的 data.

所以我们这里要做的就是呼应使用者的需求, 把这个数据取出来