继承和多态性\t\r ?
继承的意思是产生子类。简单地说,子类会具有父类的成员变量和成员函数。\t\r ?
一个程序接口指定一个函数的集合;如果一个类实现一个程序接口,那么这个类中必须具有这个程序接口指定的所有函数。多态性是指具有同一个程序接口的多种数据类型可以通过这个接口做相同的处理。\t\r ?
在接下来的三小节里,我们先介绍C++中支持继承和多态性的语法结构。然后我们进一步讨论它们的使用技巧和误区。\t\r ?
1. 继承\t\r ?
我们在定义一个类的时候可以指定它的父类,那么这个类就是一个子类,它会继承父类的成员变量和成员函数。请看下面的例子。\t\r ?
1 class A 2 { 3 public:
4 int value; 5
6 int getValue() { 7 return value; 8 } 9 }; 10
11 int main() { 12 A object;
39 class A 40 {
41 private:
42 int value = 100; 43 int value; 44
45 public: 46 A() {
47 value = 100; 48 } 49
50 protected:
13 object.value = 100; 14 cout << object. 15 getValue(); 16 } 17
18 class B : public A 19 { 20 public:
21 int value2; 22
23 int getSum() { 24 return
25 value + value2; 26 } 27 }; 28
29 int main() { 30 B object;
31 object.value = 100; 32 object.value2 = 10; 33 cout << 35 cout <<
36 object.getSum(); 37 }
51 int getValue() { 52 return value; 53 } 54 }; 55
56 int main() { 57 A object;
58 object.value = 100;
59 cout << object.getValue(); 60 } 61
62 class B : public A 63 { 64 public:
65 int value2; 66
67 int getSum() {
68 return value + value2; 69 return
70 getValue() + value2; 71 } 73 }; 74
75 int main() { 76 B object;
77 object.value = 100; 78 object.value2 = 10;
79 cout << object.getValue(); 80 cout << object.getSum(); 81 }
34 object.getValue(); 72
\t\r ?
第1--‐9行定义了一个普通的类A,它有一个公有的成员变量和一个公有的成员函数。在18--‐27行定义了一个类A的子类B。如第18行所示,定义子类的语法格式是:\t\r ?
class 子类名 : public 父类名 { … };\t\r ?
父类前面的public表示继承的方式是公有继承,这是继承的一般用法。我们暂时不讨论什么是公有继承,以及另外
的两种继承方式(它们极少被使用,基本上是没用的)。\t\r ?
在第18--‐27行的类B中,我们定义了一个公有变量和一个公有函数。因为B继承了A,所以B的每个对象一共具有两个成员变量和两个程序函数。在第29--‐37行中,我们可以看到类B的一个对象object具有成员变量value和value2,也通过object调用成员函数getValue和getSum。\t\r ?
下面我们通过第39--‐81行的程序讨论封装在继承的作用。在39--‐54行的类A中我们,成员变量value变成了私有的,而成员函数getValue变成了保护protected的。protected是除了public和private之外的另一种成员访问层次access\t\r ?level。如果一个成员的访问层次是protected的,那么它能在当前的类和当前类的子类的函数中被访问,而不能在其它函数中被访问。\t\r ?
注意到42行对成员变量的初始化方式是标准C++不允许的。这是因为,这里并不是定义变量,只是一个声明:声明所有该类的对象都具有value这个成员。如果我们需要初始化成员变量,可以在构造函数里完成,如第46--‐48行所示。\t\r ?
现在,我们看看当类A作了上述的改动后,它的子类B应该如何改动。第68行不能通过编译,原因是value在类A中是private的:它只能在类A的函数中被访问。但是,我们
可以通过函数getValue间接地取得value的值。这是因为getValue在类A中是protected的,所以它可以在类A的子类B中被访问。这里我们总结一下private和protected:\t\r ?
? 父类A中的private成员变量,实际上是存在于子类
B的每一个对象的。但是,这个对象中的成员虽然存在,却不能在子类B里面所定义的函数中被访问,而只能通过类A所提供的public和protected函数间接地访问。\t\r ?
? 父类A中的private成员函数,只能被父类里定义的
其它函数直接调用。\t\r ?
? 父类A中的protected成员函数和成员变量,被类A
的子类B里定义的函数中视为public的,而在其它的函数中被视为private的。\t\r ?
最后,介绍一些同义词。子类sub--‐class又称为派生类derived\t\r ?class,父类super--‐class又称为基类base--‐class。\t\r ?
2. 虚对象函数和函数匹配的两种方式\t\r ?
3 class A 4 {
5 public:
6 void print1() {
7 cout << \ << endl; 8 } 9
10 virtual void print2() {
11 cout << \ << endl;
12 } 13
14 void print3() {
15 cout << \ << endl; 16 } 17
18 virtual void print4() {
19 cout << \ << endl; 20 } 21 };
这下面的两个小节我们将介绍较多的概念。首先我们通过下面的例子介绍虚对象函数virtual\t\r ?object\t\r ?function的概念。\t\r ?
在类A里面,我们定义了两个函数,这两个函数分别向控制台console输出它们的函数全名,这让我们知道程序运行的时候有没有调用这个函数。其中,第10--‐12行和第18--‐20行的函数被声明为一个虚对象函数。如果类A没有子类,那么虚对象函数的作用不会显现。现在我们定义子类B。\t\r ?
23 class B : public A
24 {
25 public:
26 // not related to A::print1() 27 void print1() {
28 cout << \ << endl; 29 } 30
31 // overrides A::print2() 32 void print2() {
33 cout << \ << endl; 34 } 35 };
在子类B中,我们“重新”定义了类A中的两个函数。首先看看27--‐29行的函数,它其实跟A中的print1函数没有