第四章 基于对象的编程风格(打造一个iterator class)

翻译 2015年05月16日 14:29:06

4.6 打造一个iterator class

 

为了说明如何对class进行运算符重载操作,让我们体验一下如何实现一个iterator class我们必须提供一下操作方式:

triangular train(1,8);
triangular::iterator
it=train.begin(),
end_it=train.end();
while(it!=end_it)
{
cout<<*it<<' ';
++it;
}

为了让上述程序代码得以工作,我们必须为此iterator class定义!=,*,++等运算符。这应该如何办到呢?我们可以像定义member function那样定义运算符。运算符函数看起来很像普通函数,唯一的差别是它不用指定名称,只需在运算符前加上关键字operator即可。例如:

class triangular_iterator
{
public:
//为了不要在每次访问元素时都执行-1操作
//此处将index的值设为index-1
triangular_iterator(int index):_index(index-1){}
bool operator==(const triangular_iterator&)const;
bool operator!=(const triangular_iterator&)const;
int operator*()const;
triangular_iterator& operator++();//前置版
triangular_iterator&operator++(int);//后置版
private:
void check_integrity()const;
int_index;
}

triangular_iterator维护一个索引值,用以索引triangular中用来存储数列元素的那个static data member,也就是_elems.为了达到这个目的,triangular必须赋予triangular_operator的member function特殊的访问权限。我们将会在4.7中看到如何通过friend机制给予这种特殊的权限。如果两个triangular_operator对象的index相等,我们便说这两个对象相等:

inline bool triangular_iterator::
operator==(const triangular_iterator &rhs)const
{return_index==rhs._index;}
所谓运算符,可以直接作用于其class object:
if(train1==train2)
如果我们希望将运算符作用于指针所指的对象,就得先提取该指针,取出其所指的对象:
if(*ptril==*ptril2)
任何运算符如果和另一个运算符的性质相反,我们通常会以后者实现前者,例如:

inline bool triangular_iterator::
operator!=(const triangular_iterator &rhs)const
{return !(*this==rhs);}


以下是运算符重载的规则:


不可引用新的运算符额,除了,。*::?四个运算符,其他的运算符皆可被重载。

运算符的操作数(operator)个数不可以变,每个二元运算符都需要两个操作数,每个一元运算符都恰好需要一个操作数。因此我们无法定义出一个equality运算符,并令它接受两个以上或两个以下的操作数

运算符的优先级(precedence)不可以改变,例如,除法的运算符永远高于加法的.

运算符函数的参数列表中,必须至少有一个参数为class的类型,也就是说,我们无法为诸如指针之类的non_class类型,重新定义其已存在的运算符,当然更无法为它引进新运算符。


运算符的定义的方式,就像member function一样:
inline int triangular_iterator::
operator*()const
{
check_integrity();
return triangular::_elems[_index];
}
但也可以像non_member function一样:
inline int
operator*(const triangular_iterator &rhs)
{
rhs.check_integrity();
//注意:如果这是一个non_member function,就不具有
//访问no_public member的权利
return triangular::_elems[_index];
}

Non_member运算符的参数列表中,一定会比相应的member运算符多出一个参数,也就是this指针。对于这个member运算符而言,这个this指针隐式代表左操作数。

下面这个check_integrity()member function 可以确保_index不大于_amx_elem,并确保_elems存储了必要的元素。
inline void Triangular_iterator::
check_integrity()const
{
//第七章会介绍throw表达式
if(_index>=Triangular::_max_elems)
throw iterator_overflow();
//必要时扩展vector的容量
if(_index>=Triangular::_elems.size())
Triangular::gen_elements(_index+1);
}
接下来我们必须提供increment(递增)运算符的前置(++train)和后置(trian++)两个版本。
前置版的参数列表是空的:
inline Triangular_iterator& Triangular_iterator::
operator++()
{
//前置版本
++_index;
check_integrity();
return tmp;
}
increment(递增)或decrement(递减)运算符的前置以及后置版本都可以直接作用于class object:
++it//前置版
it__//后置版
另人生疑的是,对后置版本而言,其唯一的那个Int参数从何发生,又到哪里去呢?事实的真相是,编译器会自动为后置版本产生一个int参数(其值必为0)。用户不必为此烦恼。

    接下来我们要做的,便是为triangular提供一组begin()/end() member function,并支持前述iterator定义。这需要用到稍后才讨论到的所谓嵌套类型(nested type)。

    首先看看我们必须对triangular做的修正:

 

#include"triangular_iterator.h"

class triangular{

public:

//以下对象,可以让用户不必知晓iterator class的实际名称

typedef triangular_iterator iterator;

triangular_iterator begin() const

{

return triangular_iterator(_beg_pos);

}

triangular_iterator end() const

{

return triangular_iterator(_beg_pos+_length);

}

//......

private:

int _beg_pos;

int _length;

//....

};

嵌套类型(nested type)

typedef可以为某个类型设定另一个不同的名称。其通用形式为

typedef existing_type new_name;

其中的exisiting_type可以使任何一个内置类型,复合类型或class类型。在我们的例子中,我令iterator等同于triangular_iterator,以简化其使用形式。以下是定义一个iterator object的语法:

triangular::iterator it=train.begin();//错误

编译器就不知道在面对iterator这个字时该查看triangular的内容,于是以上声明出现错误。

如果将iterator嵌套放在每个“提供iterator抽象概念”的class内,我们就可以提供有着相同名称的多个定义。但是这样的声明语法有些复杂:

 

fibonacci::iterator fit=fib.begin();

pell::iterator pit=pel.begin();

vector<int>::iterator vit=_elems.begin();

string::iterator sit=file_name.begin();

 

Java编程思想第四版 *第五章 个人练习

练习3:(1)创建一个带默认构造器(即无参构造器)的类,在构造器中打印一条消息。为这个类创建一个对象 练习4:(1)为前一个练习中的类添加一个重载构造器,令其接受一个字符参数,并在构造器中把你自己的信...
  • zhaoqingkaitt
  • zhaoqingkaitt
  • 2014年11月03日 10:01
  • 4186

java编程思想 第四版 第六章 个人练习

java编程思想 第四版 第六章 答案,中文版 练习1:(1)在某个包中创建一个类,在这个类所处的包的外部创建该类的一个实例。 练习2:(1)将本节中的代码片段改写为完整的程序,并校验实际所发生的冲突...
  • zhaoqingkaitt
  • zhaoqingkaitt
  • 2014年11月04日 16:50
  • 2844

Java编程思想第四版*第七章*个人练习

练习1:(2)创建一个简单的类。第二个类中,将一个引用定义为第一个类的对象。运用惰性初始化来实例化 这个对象。 练习2:(2)从Detergent中继承产生一个新的类。覆盖scrub()并添加一个名为...
  • zhaoqingkaitt
  • zhaoqingkaitt
  • 2014年11月09日 11:13
  • 3078

第四章 基于对象的编程风格(什么是构造函数和析构函数)

4.2什么是构造函数和析构函数 每个数列都适合设计为class,一个数列的clas object可以表现出该数列在某范围内的元素。默认情形下,起始的位置为1.例如: Fibonacci fib1(7,...
  • Vivianluolita
  • Vivianluolita
  • 2015年04月15日 19:10
  • 272

第五章 面向对象的编程风格(定义一个抽象基类)

5.4定义一个抽象基类     本节将重新定义前一节的num_sequence class.我要为所有数列设计出共享的抽象基类,然后继承它。这该如何做到呢?     定义抽象基类的第一个步骤就是找出所...
  • Vivianluolita
  • Vivianluolita
  • 2015年05月23日 10:41
  • 420

《C++编程风格》第四章:虚函数

原书抄录: 组件之间的交互关更少并且更简单,将使得程序更容易理解和维护。(低耦合,高内聚) 当我我们要决定在一个类中到底使用数据成员还是函数成员来表示一个特性时,我们首先应该考虑: 这个特性是用属性...
  • luoluozlb
  • luoluozlb
  • 2016年07月28日 18:44
  • 254

C语言实现动态数组(以面向对象的编程风格)

/******************************************************************************* ** 程序名称:动态数组 ** 程...
  • syrchina
  • syrchina
  • 2012年03月12日 19:04
  • 1268

第二章 面向对象的编程风格(使用静态局部变量)

2.4使用局部静态对象   程序:使用局部静态对象   2.2节当中fibon_seq()函数每次被调用时,便计算出fibonacci数列(元素由用户指定),并以一个vector存储计算出来的元素,然...
  • Vivianluolita
  • Vivianluolita
  • 2015年04月19日 15:06
  • 411

c++基于对象的编程风格

c++基于对象的编程风格 知识点: 下面知识点会在代码中统一演示 1.构造函数和析构函数(构造函数的初始化语法 直接初始化和成员初始化列表) 2.mutable和const 1)为了...
  • newbieLCQ
  • newbieLCQ
  • 2017年02月09日 19:13
  • 111

《Essential C++》笔记五、基于对象的编程风格

²  构造函数:提供的一个或多个特别的初始化函数 构造函数的名称必须与class的名称相同。语法规定,构造函数不应指定返回型别,亦不需要返回任何值。它可以被重载。传参见P105 ²  成员初始化表...
  • Daisy327
  • Daisy327
  • 2013年10月14日 22:44
  • 466
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:第四章 基于对象的编程风格(打造一个iterator class)
举报原因:
原因补充:

(最多只允许输入30个字)