essential c++读书笔记4

第四章 基于对象的编程风格

前言

class 的相关事务
1、要包含头文件让程序知道它
2、class名称被视为一个类型名称,初始化可以有很多种
3、class一般由两个部分组成:一组公开的(public)操作函数和运算符,以及一组私有的实现细节。这些操作函数和运算符成为class的member function,并代表这个class的公开接口。
4、身为class的的用户,只能访问其公开接口。
5、class 的private实现细节可由member function的定义以及与此class 相关的任何数据组成

4.1 如何实现一个class

//empty() 询问是否为空

class 的声明以关键字class开始,其后接一个class名称(可任意命名): class Stack; //前置声明
前置声明后才使我们得以进行类指针的定义,或以此 class 作为数据类型。、

定义class本身:

class Stack{
    public:
	//  ...public 接口
    private//  ...private接口

}

Class定义由两个部分组成:class 的声明,以及紧接在声明之后的主体。主体部分由一对大括号括住,并以分号结尾。主体内的两个关键字public 和 private, 标示每个块的访问权限。

关于class定义
1、在class主体内定义,会自动视为inline函数,
2、在class主体之外定义member function,必须使用特殊的语法,目的是分辨该函数究竟属于哪一个class。
也要放在头文件里。

4.2 构造函数和析构函数

//构造函数

class Triangular{
public:
  // 构造函数
  Triangular();
  Triangular(int len);
  Triangular(int len,int beg_pos);
private:
  //成员
  int _length;
  int _beg_pos;
  int _next;
}

由于编辑器不会自动为我们初始化,需要自己提供一个或者多个特别的初始化函数,编辑器就会在每次class object被定义出来时,调用适当的函数加以处理。 这就是构造函数。
构造函数的函数名称与class名称相同。//不应指定返回值类型,亦不用返回任何值。
可以被重载。
注:在调用无需任何参数的构造函数时,Trianular t5(); 会出错,后面加小括号会使t5被视为函数。 Trianular t5;正确

默认构造函数
1、不接受任何参数;
2、或者为每个参数提供默认值

成员初始化列表

Triangular::Triangular(const Triangular &rhs)
  :_length(rhs._length),_beg_pos(rhs._beg_pos),_next(rhs._beg_pos-1)  {  }

紧跟在参数列表最后的冒号后面,是以逗号分隔的列表。其中,欲赋值给member的数值被放在member名称后面的小括号中。

//析构函数 destructor
主要用来释放在constructor中或对象生命周期中分配的资源。
class 名称再加上’~’前缀,它绝对不会有返回值,也没有任何参数。由于其参数列表是空的,所以也不会被重载

//成员逐一初始化

Triangular tri1(8);
Triangular tri2 = tri1;

会将tri1里面的参数依次复制到tri2.

{
  Matrix mat(4,4);
    //构造函数发生作用
  {
	Matrix mat2 = mat;
	//进行默认成员初始化
	//使用mat2
	//mat2的析构函数发生作用
  }
   //使用mat
   //mat的析构函数发生作用

}

这样就导致mat2 和 mat 都指向heap内的同一个数组,mat2使用完析构,mat仍 那个数组

怎么解决?
可以通过"为Matrix提供另一个copy constructor"达到目的。

Matrix::Matrix(const Matrix &rhs)
  :_row(rhs._row),_col(rhs.col)
{  //对rhs.pmat所指的数组产生一份完全副本
    int elem_cnt = row * _col;
    _pmat = new double[elem_cnt];

    for (int ix = 0;ix < elem_cnt; ++ix)
	_pmat[ix] = rhs._pmat[ix];
}

4.3 mutable(可变) 和 const (不变)

int sum(const Triangular &trian)
{
    int beg_pos = trian.beg_pos();
    int length = trian.length();
    int sum = 0;
    
    for (int ix = 0;ix < length;++ix)
	sum += trian.elem(beg_pos + ix);
    return sum;
}

可以看出trian是个const reference参数,要保证trian在sum()之中不被修改。所以要确保beg_pos(),length(),elem()不会改变其调用者。

class Triangular{
public:
  // const member function
  int length() const {return _length;}
  int beg_pos() const {return _beg_pos;} 
  int elem(int pos) const;

  //non-const member function
  bool next(int &val);
  void next_reset(){_next = _beg_pos - 1;}
private:
  //成员
  int _length;
  int _beg_pos;
  int _next;
}

const 修饰符紧接于函数参数列表后。凡是在class主体之外定义的,如果是一个const member function,那就必须同时在声明与定义中指定const。

int Triangular::elem(int pos) const
{return _elem[pos-1];}

//可变的数据成员

class Triangular{
public:
  // const member function
  int length() const {return _length;}
  int beg_pos() const {return _beg_pos;} 
  int elem(int pos) const;

  //non-const member function
  bool next(int &val);
  void next_reset()  {_next = _beg_pos - 1;}
private:
  //成员
  int _length;
  int _beg_pos;
  int _next;
}
int sum(const Triangular &trian)
{
    if(!trian.length() )
	return 0;

    int val,sum = 0;
    trian.next_reset();
    while(trian.next(val))
	sum += val;
    return sum;
}

会运行错误! trian是个const object,而next()、next_reset()都会改变_next的值,他们都不是const object。但它们都被trian调用,就会造成错误。
想要实现sum(),next()、next_reset()就得是const object。
于是:
将_next声明为mutable,next()、next_reset()声明为const object就可以了

class Triangular{
public:
  // const member function
  int length() const {return _length;}
  int beg_pos() const {return _beg_pos;} 
  int elem(int pos) const;

  //non-const member function
  bool next(int &val) const;
  void next_reset() const {_next = _beg_pos - 1;}
private:
  //成员
  int _length;
  int _beg_pos;
  mutable int _next;
}

int sum(const Triangular &trian)
{
    if(!trian.length() )
	return 0;

    int val,sum = 0;
    trian.next_reset();
    while(trian.next(val))
	sum += val;
    return sum;
}

4.4 什么是this指针

this 指针是在member function内用来指向其调用者(一个对象)。
在member function内,this指针可以让我们访问其调用者的一切,如果想在copy()中返回tr1,只要简单提领this指针就可以。
return *this;
欲以一个对象复制另一个对象,先确定两个对象是否相同:

Triangular & Triangular ::
copy(const Triangular &rhs)
{
    if(this != &rhs)
	{
	  _length = rhs._length;
	  _beg_pos = rhs._beg_pos;
	  _next = _rhs._beg_pos - 1;	
	}
    return *this;
}

4.5 静态成员

static data member用来表示唯一的、可共享的member,,它可以在同一类的所有对象中被访问。

class Triangular{
public
  //...
private:
  static vector<int> _elems;
}

对于class而言,static data member只有唯一的一份实体。在程序代码定义:

vector<int> Triangular::_elems;

也可以为它指定初值:

int Triangular::_initial_size = 8;

4.6 Iterator Class 迭代器

怎么定义运算符?
和普通函数一样,唯一的差别是它不用指定名称,只需在运算符前加上关键字operator即可。

运算符重载的规则:
1、不可以引入新的运算符。
2、运算符的操作数个数不可改变。
3、运算符的优先级不可改变。
4、运算符的参数 中,必须至少有一个参数为class类型。
运算符的定义方式:

inline int Triangular_iterator::
operator*() const
{
    check_interity();
    return Triangular::_elem[_index];
}

//嵌套类型
typedef 可以为某个类型设定另一个不同的名称。同样形式为: typedef existing_type new_name; 创建易于记忆的类型名,用它来归档程序员的意图。
其中existing_type可以是任何一个内置类型、复合类型或class类型。

4.7 合作关系必须建立在友谊的基础上

要想访问class的private member,必须将函数声明为“friend”
只要在某个函数的原型前加上关键字friend,就可以将它声明为某个class的friend。这份声明可以出现在class定义的任意位置,不受private或public的影响。

怎么令class A与class B建立friend关系,借此让class A的所有member function都成为class B的

friend
class A{
    friend class B;
}

4.8 实现一个copy assignment operator 复制赋值运算符

对于Matrix,同样需要一个copy constructor 和一个copy assignment operator。

Matrix::Matrix(const Matrix &rhs)
{  
    if(this != &rhs)
      {
         _row = rhs._row;_col = rhs._col;
	 int elem_cnt = _row * _col;
	 delete [] _pmat;
	 _pmat = new double[elem_cnt];
	 for (int ix = 0;ix < elem_cnt; ++ix)
	   _pmat[ix] = rhs._pmat[ix];
      } 
    return *this;
}

4.9 实现一个function object

所谓function object,乃是一种“提供有function call 运算符”的class。

4.10 重载iostream 运算符

想实现 cout << trian << endl;
需要重载output 运算符

ostream& operator << (ostream &os,const Triangular &rhs)
{
    os << "(" << rhs.beg_pos() << "," << rhs.length() << ",";
    rhs.display(rhs.lenght(),rhs.beg_pos(),os);
    return os;
}

传入函数的ostream对象又被原封不动的返回。

4.11 指针,指向Class Memeber Function

实现一个通用的数列类 num_sequence,使其对象可同时支持前面的六种数列。
typedef void (num_sequence::*PtrType)(int); 将PtrType声明为一个指针,指向num_sequence的member function,后者返回类型是void,只接受一个参数。

PtrType pm = 0;

将PtrType声明为一个指针,指向num_sequence的member function,后者的返回类型是void,只接受一个int参数。

class num_sequence{
public:
    
    typedef void (num_sequence::*PtrType)(int);

    //_pmf可指向下列任何一个函数
    void fi(int);
    void pell(int);
    void lucas(int);
    void tri(int);
    void sequ(int);
    void pent(int);

private:
    PtrType _pmf;
}

为了取得某个member function的地址,我们对函数名称应用 取址 运算符。

PtrType pm = &num_sequence::fi;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值