继上篇的文章​​《Essential C++ 笔记(4):基于对象的编程风格(上)》​​,继续更新Essential C++ 笔记。

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

  任何​​class​​均可以将其他函数或其他类指定为它的朋友(friend)。所谓的​​friend​​就具备了和类成员函数相同的访问权限。在类的内部必须是将非成员函数设为朋友。

class Triangular {
friend int operator*(const Triangular_iterator &rhs);
};
class Triangular_iterator {
friend int operator*(const Triangular_iterator &rhs);
};

//类的外部定义
inline int operator*(const Triangular_iterator &rhs){
rhs.check_integrity(); // 可以直接访问类的私有成员
return Triangular_iterator::_elems[rhs.index()]; // 可以直接访问类的私有成员
}

  可以先令A类与B类建立友元关系,让A类的所有成员函数称为B类的友元。

class Triangular {
//Triangular_iterator中的所有成员函数都是Triangular中的友元。
friend class Triangular_iterator;
};

8、实现一个Copy assignment operator(拷贝赋值运算符)

  下面的代码属于默认的成员逐一赋值操作:

Triangular tri1(8), tri2(8, 9);
tri1 = tri2;

  但对于第二节中的Matrix类,上述拷贝过程只是浅拷贝,我们需要的是深拷贝,这时需要一个拷贝构造函数和一个拷贝赋值运算符:

Matrix& Matrix::operator=(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;
}

9、实现一个函数对象(function object)

  编译器在编译过程中遇到函数调用,例如:

It(ival);

​It​​可能是函数名,可能是函数指针,也可能是提供了仿函数(function call)运算符的函数对象。

  仿函数运算符可以接收任意个数的参数。举个例子,测试传入值是否小于某测试值。

class LessThan{
public:
LessThan(int val) : _val(val) {} //成员初始化列表
int comp_val() const{
return _val; // 基值的读取
}
void comp_val(int nval){
_val = nval;// 基值的写入
}
bool operator()(int _value) const;
private:
int _val;
};
//仿函数运算符实现如下
inline bool LessThan::operator()(int value) const{
return value < _val;
}

  将仿函数运算符应用到对象身上,便可调用仿函数运算符:

int count_less_than(const vector<int> &vec, int comp){
LessThan It(comp);
int count = 0;
for(int ix = 0; ix < vec.size(); ix++)
if(It(vec[ix]))
count++;
return count;
}

  通常将函数对象当做参数传给泛型算法:

void print_less_than(const vector<int>& vec, int comp, ostream &os = cout){
LessThan It(comp);
vector<int>::const_iterator iter = vec.begin();
vector<int>::const_iterator it_end = vec.end();

os << "elements less than " << It.comp_val() << endl;
while((iter != find_if(iter, it_end, It)) != it_end){
os << *iter << ' ';
++iter;
}
}

10、重载iostream运算符

  如果想要读取和写入类对象的值,我们希望​​cout << trian << endl;​​这样写,所以需要重载output运算符操作:

ostream& operator<<(ostream &os, const Triangular &rhs){
os << "(" << rhs.beg_pos() << ", " << rhs.length() << " )";
rhs.display(rhs.length(), rhs.beg_pos(), os);
return os;
}
// 实现如下:
Triangular tri( 6, 3 );
cout << tri << endl;
// 结果:
// ( 3, 6 ) 6 10 15 21 28 36

11、指针,指向类成员函数

  使用一个通用的数列类​​num_sequence​​,使其对象可同时支持多种数列:

int main(){
num_sequence ns;
const int pos = 8;
for(int ix = 0; ix < num_sequence::num_of_sequence(); ++ix){
ns.set_sequence(num_sequence::ns_type(ix));
int elem_val = ns.elem(pos);
display(cout, ns, pos, elem_val);
}
}

  指向成员函数的指针,要指定它所指向的是什么类:

void (num_sequence::*pm)(int) = 0;

该程序就是将​​pm​​​声明为一个指针,指向​​num_sequence​​​的成员函数,返回类型是​​void​​​,只接受一个类型为​​int​​​的参数。​​pm​​​初始值为​​0​​,表示目前不指向任何成员函数。

  可以对上述代码进行简化:

typedef void (num_sequence::*PtrType)(int);
PtrType pm = 0;

  ​​num_sequence​​​提供下述六个成员函数,每一个都可由​​PtrType​​指针加以定位:

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

void fibonacci(int);
void pell(int);
void lucas(int);
void triangular(int);
void sequence(int);
void pentagonal(int);
private:
PtrType _pmf;
};

  如果需要定义一个指针,指向成员函数​​fibonacci()​​:

PtrType pm = &num_sequence::fibonacci;

  可以将六个成员函数的地址存储到一个​​static array​​​中。再维护一个​​vector​​,存储各个数列:

class num_sequence{
public:
typedef void (num_sequence::*PtrType)(int);
//...
private:
vector<int>* _elem; // 指向目前所用的vector
PtrType _pmf; //指向目前所用的算法(用以计算数列的元素)
static const int num_seq = 7;
static PtrType func_tbl[num_seq];
static vector<vector<int> > seq;
};

  接下来提供每个静态成员函数的定义:

const int num_sequence::num_seq;
vector<vector<int> > num_sequence::seq(num_seq);

num_sequence::PtrType num_sequence::func_tbl[num_seq] = {
0,
&num_sequence::fibonacci,
&num_sequence::pell,
&num_sequence::lucas,
&num_sequence::triangular,
&num_sequence::sequence,
&num_sequence::pentagonal
};

  ​​_elem()​​​和​​_pmf​​​在​​set_sequence()​​​中一起被设定,前者指向存有数列元素的​​vector​​,后者指向产生数列元素的成员函数。

  指向成员函数和指向函数的指针的一个不同点就是,前者必须通过同一类的对象加以调用,该对象便是成员函数中的​​this​​指针所指之物。假设如下定义:

num_sequence ns;
num_sequence *pns = &ns;
PtrType pm = &num_sequence::fibonacci;

  通过​​ns​​​调用​​_pmf​​:

// 和 ns.fibonacci(pos) 相同
(ns.*pm)(pos)
// 和 pns->fibonacci(pos)相同
(pns->*pm)(pos)

  下面是​​elem()​​函数的实现内容:

int num_sequence::elem(int pos){
if(!check_integrity(pos))
return 0;
if(pos > _elem->size())
(this->*_pmf)(pos);
return (*_elem)[pos - 1];
}

  以上便是该书的第四章内容,下篇更新第五章的内容,to be continued…