第4章 基于对象的编程风格
这一章,我们会设计并属于实现我们自己的class。
在之前的几章,我们已经指定Class的一些相关事项。
1、使用Class之前,要包含相应的头文件
2、class名称被视为一个类型,就像int,double一样。
3、class会提供一组操作函数,让我们作用于其object上。
4、class由两部分组成:一组公开的操作函数;一组私有的实现细节。
4.1 如何实现一个Class
我们从实现一个 栈(stack)开始,实现一个class。
什么是栈呢,栈是一种存放数据的结构,它允许我们在里面存放数值,并以 后进先出的顺序取出。我们以pushing 方式存入数值,以popping方式取出数值。
用户可能还需要其他操作,如查询stack空间是否已满(full),是否为空(empty),查询stack元素个数(size)。
Class的声明以关键字 class开始,随后接一个class名称:
class stack;
class的定义类似这样:
class Stack {
public:
//…public接口
private:
//…private实现部分
};
class 定义有两部分:class声明,主体。
主体中的public和private是访问权限,public 可被程序任何地方访问,private只能被class内部或class friend 访问。
stack class的定义如下:
class Stack{
public:
bool push(const string&);
bool pop(string &elem);
bool peek(string &elem);
bool empty();
bool full();
int size() { return _stack.size();}
private:
vector<string> _stack;
};
所有的member function都要在Class内声明,如果在Class内定义,则会自动被视为inline函数。在Class外定义,必须用特殊的语法:
inline bool
Stack::empty()
{
return _stack.empty();
}
bool
Stack::pop(string &elem)
{
if(empty())
return false;
elem = _stack.back();
_stack.pop_back();
return true;
}
Inline函数和Class定义 都放在对应的,h文件中。
非inline函数应该放在和class同名的.cpp文件中。
下面是Stack member function 的定义。
inline bool Stack::full()
{return _stack.size() == _stack.max_size();}
bool Stack::peek(string &elem)
{
if(empty())
return false;
elem = _stack.back();
return true;
}
bool Stack::push(const string &elem)
{
if(full())
return false;
_stack.push_back(elem);
return true;
}
4.2 构造函数和析构函数
编译器会在每次class object被定义时,调用构造函数(constructor)来进行初始化。
constructor(构造函数)的名称必须和Class名称相同。constructor没有返回值类型,可以被重载
最简单的constructor是default constructor,它不需要接受参数。
参数表为空 或者为每个参数提供默认值。
Member initialization List(成员初始化列表)
Triangular::Triangular(const Triangular &rhs)
:_length(rhs._length),_beg_pos(rhs._beg_pos),_next(rhs._beg_pos-1)
{}
Member initialization list 紧接在参数列表的最后的冒号后面,是个以逗号分隔的列表。
destructor(析构函数)
析构函数是class名称加上~前缀,没有返回值,也没有参数。用来释放对象的资源(释放内存。析构函数由系统自动调用。
Memberwise initialization(成员逐一初始化)
即当使用一个对象给另一个对象初始化时,对象中的成员会逐一复制。
有时默认的复制操作不符合我们的要求,我们要自定义copy constructor。
类似于:
Matrix:Matrix(const Martrix &rhs){
}
4,3 mutable(可变)可const(不变)
class 设计者在Member function身上标注const,告诉编译器,这个Member function 不会更改Class Object中的内容。
const修饰符写于]函数参数列表之后,凡是在Class主体以外定义者,如果他是一个const member function ,它必须在声明和定义处都指定const。
Member function 可以根据const与否而重载,因此可以设计这样的重载函数:
const BigClass& val() cosnt(return _val);
BigClass& val(){return _val};
针对const的mutable, 将某个成员标示为mutable,就可以宣称,对这个成员的修改不会破坏class object的常量性。(在const member function中可以修改这个成员)
4.4 this指针
this指针时Member function内用来指向其调用者的指针。
4.5 静态类成员
static(静态)data member用来表示唯一的,可以共享的Member。它可以在同一类的所有对象中被访问。
对Class而言,static data member只有唯一的一份实体。因此我们必须在代码文件中提供清楚的定义。
//.cpp
vector<int> Triangular::elems;
如果在class member function内部访问static data member,其访问方式和访问一般数据成员相同。
const static int data member可以在声明时指定初值。
Static Member function(静态成员函数)
static可以不用任何具体的对象调用:
如 Triangular::is_elem(7);
并且为了不和具体的对象有关,static Member function 不能访问non-static member。
static Member function的声明方式是在原函数前加关键字static。
在Class主体外进行定义时,无需再加static(此规则也适用于static data member)。
,
4.6 打造一个Iterator Class
我们可以像定义Member function那样定义运算符,运算符函数和普通函数很像,区别是它的名称就是operator加运算符号。
bool operator==(const Triangular_iterator &) const;
int operatir*()const;
运算符重载的规则:
1、不能引入新的运算符
2、运算符操作数个数不可变
3、运算符优先级不变
4、运算符函数的参数列表至少有一个是class类型的。
运算符定义的方式可以向Member function一样,
也可以像non member function一样
Non member function 运算符的参数列表中,一定会比Member运算符多一个参数,也就是this指针。对于Member运算符来说,这个this指针隐式代表左操作数。
前++ 和后++
前++的参数表是空的,
后++的参数表得有一个int参数 inline Triangula_iterator Tirangular_iterator::
operator++(int)
{..
return …;
}
但使用时不需要传入这个int参数,编译器自动设置为0。
直接使用
it++;
即可。
4.7 friend 友类
class可以将其他function或class指定为friend,这样就可以让他们具备和class member function相同的访问权限,可以访问class 的private member。
只要在某个函数的原型前加上关键字friend就可以将它声明为某个class的关键字。
如果让class A 认为class B是自己的friend,则class B的所有函数都是A的friend。
4.8实现复制运算符(copy assignment operator)
只要为Class提供copy assignmemnt operator,它就会被用来取代默认的memberwise copy。
4.9 实现一个function object
function object 是一种提供有function call运算符的class。
通常我们将function object 作为参数传递给泛型算法。
function call运算符:
例:
inline bool LessThan::operator()(int value) const {return value<_val;}
4.10 重载iostream运算符
为了让我们的class支持
cout<<train<<endl;这种形式,我们需要重载iostream运算符。
ostream & operator<<(ostream &os, const Triangular &rhs)
{
os<<”(“<<rhs._beg_pos()<<”,”<<rhs.length()<<”,”;
rhs.display(rhs.length(),rhs._beg_pos(),os);
return os;
}
类似的可以重载>>运算符。
4.11 指针,指向Class Member Function
指向成员函数的指针和指向普通函数的指针很像,都需要指定返回类型和参数列表。
不过指向成员函数的指针还要指定 所属的class
如:
void (num_sequence::*pm)(int) = 0;
取得某个member function 的地址,对函数名词使用&运算符。,函数名称前要加class scope运算符限定。
pm = &num_sequence::Fibonacci;