C++中构造的类一般会像两种东西,像指针(称为pointer-like classes,指针类),或者像函数(称为function-like classes,函数类)。一个类被设计成指针,被当作指针来使用,是因为我们想在传统指针的基础上多实现功能,或者说想对指针有更多的控制。常见的指针类有两种:智能指针和迭代器。
关于智能指针
C++中常见的智能指针有auto_ptr,scoped_ptr,shared_ptr等等。下面来介绍shared_ptr,智能指针内部一定有一个真正的指针,图示为:
其中,大圆圈表示一个智能指针,里面包含一个真正的指针px,指向实际所指的对象。
对于智能指针,和传统的指针一样,会使用两种操作符,*解引用(dereference)操作符和->成员访问操作符。在智能指针类中要提供这两种操作符重载operator* ( )和operator-> ( )
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* p;
long* pn;
...
};
在智能指针shared_ptr内有一个真正的指针px,指向对象T。下面的代码示范了智能指针的用法:
struct Foo
{
...
void method(void) {...}
};
shared_ptr<Foo> sp(new Foo);//定义了一个指向Foo类型的智能指针
Foo f(*sp);//定义了一个Foo对象,并使用智能指针sp所指的对象来初始化
sp->method();//相当于px->method();
现在有一个类Foo,第1行产生了一个天然的指针sp,通过调用智能指针的构造函数,我们把天然的指针包装在智能指针内。第2行当我们解引用*sp,就相当于return *px。第3行sp->method();的意思是想通过智能指针sp调用成员函数method,首先会调用操作符重载operator->( ) const,得到智能指针内部真正的指针px,然后再通过px调用函数method(px->method();)。在C++中规定,对于调用->操作符之后,->操作符并不会消耗掉,而是继续作用下去。
关于迭代器
迭代器指向容器中的元素,可以被看做智能指针。不过迭代器除了要提供*和->操作外,还要提供++,--(用于指针移动,可以遍历容器),==和!=操作。下面的代码以链表的迭代器为例:
template<class T>
struct __list_node
{
void* prev;
void* next;
T data;
};
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef __list_iterator<T, Ref, Ptr> self;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
link_type node;
bool operator== (const self& x) const { return node == x.node; }
bool operator!= (const self& x) const { return node != x.node; }
reference operator*() const { return (*node).data; }
pointer operator->() const { return &(operator*()); }
self& operator++() { node = (link_type)((*node).next); return *this; }
self operator++(int) {self tmp = *this; ++*this; return tmp; }
self& operator--() { node = (link_type)((*node).prev); return *this; }
self operator--(int) {self tmp = *this; --*this; return tmp; }
};
链表的数据结构如图所示:
链表的节点包含三个部分,分别是前一个节点的指针prev,后一个节点的指针next,和数据data。图示中的“大泡泡”表示迭代器_list_iterator,其中包含一个真实的指针node,用来指向链表中的节点。从程序中可以看到,_list_iterator除了提供*和->操作,还提供了++,--操作。对迭代器进行++操作,会指向下一个元素,对迭代器进行--操作,会指向前一个元素。由于node只是指向整个节点,为了取得节点的数据,还需要调用(*node).data
_list_iterator的使用示例代码如下:
struct Foo
{
void method()
{ cout << "method" << endl; }
};
ll::_list_iterator<Foo, Foo&, Foo*> iter;
*iter; //获得一个Foo对象
iter->method(); //意思是调用Foo::method(),相当于(*iter).method,也相当于(&(*iter))->method()
首先,我们声明一个_list_iterator迭代器iter。我们知道迭代器也是一种指针类,可以模拟指针的操作。通过*操作符,我们可以得到一个Foo对象。