您的位置:首页 > 编程语言 > C语言/C++

C++ Primer Plus -- 第14章 C++的代码重用 笔记

2010-09-03 00:37 309 查看
C++的一个主要目标是促进代码重用。公有继承是实现这种目标的机制之一,其他还有:本身是另一个类的对象。这种方法称为包含、组合或层次化。另一种方法是使用私有或保护继承。通常,包含、私有继承和保护继承用于实现has-a关系,即新的类将包含另一种类的对象。多重继承使得能够使用两个或更多的基类派生出新的类,将基类的功能组合在一起。
14.1 包含对象成员的类
主要介绍了valarray类,该类是由头文件valarray支持的。valarray被定义为一个模板类,以便能够处理不同的数据类型。模板特性意味着声明对象时,必须指定具体的数据类型。这个类模板的一些方法:
1: operator[]; // 访问数组中的元素


2: size(); // 返回数组包含的数据


3: sum(); // 返回所有数据的总和


4: max(); // 最大值


5: min(); // 最小值


接下来讲了一个Student的范例,使用包含的关系。

要注意的两点:1)初始化被包含对象,使用初始化列表来初始化包含对象。对于继承对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数。

1: hasDMA::hasDMA(const hasDMA & hs):baseDMA(hs) {...}


对于成员对象,构造函数则使用成员名

1: Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) {}


2)使用被包含对象接口:被包含对象的接口是公有的,可以在类方法中使用它。

14.2 私有继承

使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会称为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。简而言之,派生类将继承基类的接口;这是is-a关系的一部分。使用私有继承,基类的公有方法将成为派生类的私有方法。简而言之,派生类不继承基类的接口。使用私有继承,将继承实现。包含将对象作为一个命名的成员对象添加到类中,而私有继承将对象作为一个未被命名的继承对象添加到类中。

注意的问题:1)初始化基类组件,也是使用成员初始化列表的句法,它使用类名而不是成员名来标识构造函数:

1: Student(const char * str, const double * pd, int n):std::string(str), ArrayDb(pd, n) {}


2)访问基类的方法

使用私有继承时,只能在派生类的方法中使用基类方法。对比一下包含与私有继承调用基类方法的区别:

(1) 包含

1: double Student::Average() const


2: {


3:     if(scores.size() > 0)


4:         return scores.sum() / scores.size();


5:     else


6:         return 0;


7: }


(2) 私有继承

1: typedef std::valarray ArrayDb;


2: double Student::Average() const


3: {


4:     if(ArrayDb::size() > 0)


5:         return ArrayDb::sum() / ArrayDb::size();


6:     else


7:         return 0;


8: }


3)访问基类对象

使用作用域解析操作符可以访问基类的方法,但如果要使用基类对象本身,该如何做呢?答案是使用强制类型转换。前面介绍过,指针this指向用来调用方法的对象,因此*this为用来调用方法的对象,在下例中,为类型为Student的对象。为避免调用构造函数创建新的对象,可以使用强制类型转换来创建一个引用:

1: const string & Student::Name() const


2: {


3:     return (const string &) *this;


4: }


4)访问基类的友元函数

用类名显式地限定函数名不适合于友元函数,这是因为友元不属于类。不过可以通过显式地转换为基类调用正确的函数。

1: ostream & operator<<(ostream & os, const Student & stu)


2: {


3:     os << "Scores for " << (const string &) stu << ":/n";


4:     ...


5: }


引用stu不会自动转换为string引用。根本原因在于,在私有继承中,在不进行显式类型转换的情况下,不能将指向派生类的引用或指针赋给基类引用或指针。不过,即使这个例子使用的是公有继承,也必须使用显式类型转换。原因之一是,如果不使用类型转换,会导致递归调用。另一个原因是,由于这个类使用的是多重继承,编译器将无法确定应转换成那个基类,如果两个基类都提供了函数operator<<()。

5)使用包含还是私有继承

一般都选择包含。首先是易于理解。其次是继承会引起许多问题,尤其是从多个基类继承时。另外,包含能够包括多个同类的子对象。而继承只能使用一个这样的对象。不过私有继承也有很多优点:通过继承得到的是派生类,因此它可以访问保护成员。另一种需要使用私有继承的情况是需要重新定义虚函数。

6)保护继承

使用保护继承时,基类的公有成员和保护成员都将成为派生类的保护成员。与私有继承的主要区别在于:使用私有继承时,第三代类将不能使用基类的接口,这是因为基类的公有方法在派生类中将变为私有方法;使用保护继承时,基类的公有方法在第二代中将变成保护的,因此第三代派生类可以使用它们。

7)使用using重新定义访问权限

假设要让基类的方法在派生类外面可用,方法之一就是定义一个使用该基类方法的派生类方法。如下:

1: double Student::sum() const


2: {


3:     return std::valarray::sum();


4: }


另一种方法是。将函数调用包装在另一个函数调用中,即使用一个using声明来指出派生类可以使用特定的基类成员,即使采用的是私有派生。如下:

1: class Student:private std::string, private std::valarray


2: {


3:     ...


4: public:


5:     using std::valarray::min;


6:     using std::valarray::max;


7:     ...


8: }


上述using声明使得valarray::min()和valarray::max()可用,就像它们是Student的公有方法一样:

1: cout << "high score: " << ada[i].max() << endl;


注意using声明只使用成员名--没有圆括号、函数特征标和返回类型。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: