Essential C++ 笔记(5):基于对象的编程风格(下)

  继上篇的文章《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()_pmfset_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…

你可能感兴趣的:(C++)