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

C++快速入门 (十三) 继承和多态

2013-02-25 14:03 309 查看

一,继承

(1). 继承的基本语法

继承是面向对象的基本特征之一。简单的继承示例如下:

[align=left]class ExampleBase[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int x;[/align]
[align=left]public :[/align]
[align=left] ExampleBase( ) : x(0)[/align]
[align=left] {[/align]
[align=left] cout << "ExampleBase 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left] void SetX ( int n )[/align]
[align=left] {[/align]
[align=left] x = n ;[/align]
[align=left] }[/align]
[align=left] int GetNum()[/align]
[align=left] {[/align]
[align=left] return x;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class Example : public ExampleBase[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int y;[/align]
[align=left]public :[/align]
[align=left] Example( ):y(10)[/align]
[align=left] {[/align]
[align=left] cout << "Example 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left] int GetNum()[/align]
[align=left] {[/align]
[align=left] return y;[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Example ex;[/align]
[align=left] cout << ex.GetNum() << endl;[/align]
[align=left] return 0;[/align]
[align=left]};[/align]

[align=left]// return:[/align]
[align=left]// ExampleBase 类构造函数[/align]
[align=left]// Example 类构造函数[/align]
[align=left]// Example 类函数[/align]
[align=left]// 10[/align]

总结:

创建派生类实例时,构造函数调用从最底层基类开始直至当前类。清理对象时顺序相反。
基类的访问修饰符对派生类依然有效。如 基类中的私有成员即使是派生类中依然无法访问。
派生类中只要有和基类同名的函数,即便是签名不同(参数或返回值不同),对于派生类对象,基类的同名函数将会被隐藏(和重载不同)。

你可以在派生类中通过作用域操作符调用基类的函数

[align=left]Base ::GetNum();[/align]

(2). protected 修饰符

声明为 protected 的成员可以让从其派生的类访问。而在其他地方和声明为 private 一样外界无法访问(可参见下节示例)。

(3). 派生类中显式调用基类的构造函数

当基类中存在多个构造函数时,就有可能需要在派生类中调用指定的基类构造函数。方法如下:

[align=left]class ExampleBase[/align]
[align=left]{[/align]
[align=left]protected :[/align]
[align=left] int x;[/align]
[align=left]public :[/align]
[align=left] ExampleBase( ) : x(0)[/align]
[align=left] {[/align]
[align=left] cout << "ExampleBase 类无参数 构造函数" << endl;[/align]
[align=left] }[/align]
[align=left] ExampleBase(int n ) : x( n )[/align]
[align=left] {[/align]
[align=left] cout << "ExampleBase 类有参数 构造函数" << endl;[/align]
[align=left] }[/align]
[align=left] void SetX ( int n )[/align]
[align=left] {[/align]
[align=left] cout << "ExampleBase 类函数" << endl;[/align]
[align=left] x = n ;[/align]
[align=left] }[/align]
[align=left] int GetNum()[/align]
[align=left] {[/align]
[align=left] return x;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class Example : public ExampleBase[/align]
[align=left]{[/align]
[align=left]private :[/align]
[align=left] int y;[/align]
[align=left]public :[/align]
[align=left] Example( ):y(10),ExampleBase (15)[/align]
[align=left] {[/align]
[align=left] cout << "Example 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left] int GetNum()[/align]
[align=left] {[/align]
[align=left] cout << "Example 类函数" << endl;[/align]
[align=left] return y;[/align]
[align=left] }[/align]
[align=left] int SumXy()[/align]
[align=left] {[/align]
[align=left] return x + y;[/align]
[align=left] }[/align]
[align=left]};[/align]

(4). 多重继承

C++中允许多重继承,既 一个类可以有多个基类,

[align=left]class A[/align]
[align=left]{[/align]
[align=left]protected :[/align]
[align=left] int x;[/align]
[align=left]public :[/align]
[align=left] A(): x(1)[/align]
[align=left] {[/align]
[align=left] cout << "A 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class B[/align]
[align=left]{[/align]
[align=left]protected :[/align]
[align=left] int y;[/align]
[align=left]public :[/align]
[align=left] B(): y(2)[/align]
[align=left] {[/align]
[align=left] cout << "B 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class C[/align]
[align=left]{[/align]
[align=left]protected :[/align]
[align=left] int z;[/align]
[align=left]public :[/align]
[align=left] C(): z(3)[/align]
[align=left] {[/align]
[align=left] cout << "C 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class Example : public A , public B, public C[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] Example( )[/align]
[align=left] {[/align]
[align=left] cout << "Example 类构造函数" << endl;[/align]
[align=left] }[/align]
[align=left] int SumXyz()[/align]
[align=left] {[/align]
[align=left] return x + y + z;[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Example ex;[/align]
[align=left] cout << ex.SumXyz();[/align]
[align=left]};[/align]
[align=left]//return:[/align]
[align=left]//A 类构造函数[/align]
[align=left]//B 类构造函数[/align]
[align=left]//C 类构造函数[/align]
[align=left]//Example 类构造函数[/align]
[align=left]//6[/align]

后边还会继续讨论多重继承。

二,多态

(1). 多态的基本理念

多态就是 可以用基类对象表示其派生类对象,在调用该对象上的函数时能正确调用派生类对象的相应函数(动态绑定)的一种能力。
典型的应用场景如: 某个函数的形参为基类类型(指针或引用),当调用时可以为其传入该基类的派生类并能正确调用。很显然一般的函数覆盖是实现不了动态绑定的,于是 C++引入了虚函数来完成动态绑定。

(2). 虚函数 virtual

C++中使用关键字 virtual 声明虚函数,

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] virtual void ShowInfo()[/align]
[align=left] {[/align]
[align=left] cout << "is Base class" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class ExampleOne : public Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] void ShowInfo()[/align]
[align=left] {[/align]
[align=left] cout << "is ExampleOne class" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class ExampleTwo : public Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] void ShowInfo(int x)[/align]
[align=left] {[/align]
[align=left] cout << "is ExampleTwo class" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Base *b = new ExampleOne ;[/align]
[align=left] b->ShowInfo();[/align]
[align=left] Base *b2 = new ExampleTwo ;[/align]
[align=left] b2->ShowInfo();[/align]
[align=left]}[/align]
[align=left]//return:[/align]
[align=left]//is ExampleOne class[/align]
[align=left]//is Base class[/align]

总结:

只有虚函数才具有多态(动态绑定)性。
只有派生类函数和基类中的虚函数签名完全相同时才会具有多态性,
用于覆盖基类虚函数的派生类函数也可以声明为虚函数。
析构函数也可以为多态的,所以一般的基类析构函数应为虚函数。
构造函数或析构函数中调用虚函数时,将只会调用其类型上的版本。

顺便提一句。即便是已经实现了动态绑定,也可以用如下语法显式的指定应该调用哪个类的函数

[align=left]b->Base::ShowInfo();[/align]

(3). 纯虚函数

在虚函数后加 "= 0" 时,该虚函数就为纯虚函数。因为纯虚函数没有方法体自然就没法调用执行。这样的类是不完全的,所以拥有纯虚函数的类会变成了抽象类(不能被实例化的类)。

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] virtual void ShowInfo() = 0;[/align]
[align=left]};[/align]

当一个类继承自抽象类并且未覆盖其纯虚函数时,该类也会变成抽象类(显而易见)。

(4). 虚继承

当一个派生类间接继承多个相同基类时,该基类会在内存中有多个副本。

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] int num;[/align]
[align=left]};[/align]
class MidOne : public Base {
};
class MidTwo : public Base {
};
[align=left]class Example : public MidOne , public MidTwo { };[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Example *ex = new Example ;[/align]
[align=left] cout << ex->num; [/align]
[align=left] // error C2385: 对“num”的访问不明确, 因为有多个 Base的副本[/align]
[align=left]}[/align]

可以通过 继承时添加 修饰符 virtual ,使内存只存储一个该基类的副本。

(5). 动态绑定原理

当类中有虚函数时(动态绑定基础),会生成一个保存该类所有虚函数(包括继承来未被覆盖的)地址的虚表(vtable),然后用一个指针(vptr)指向虚表,并将(大多数情况)该指针放在类的首地址。当将派生对象赋给基类对象时实际只是改变了基类对象的引用(指向派生类对象地址),这样就可以调用正确的虚函数,从而实现动态绑定。

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] virtual void ShowInfo()[/align]
[align=left] {[/align]
[align=left] cout << "is Base" << endl;[/align]
[align=left] }[/align]
[align=left] virtual void Say()[/align]
[align=left] {[/align]
[align=left] cout << "is Base say" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class Example : virtual public Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] virtual void ShowInfo()[/align]
[align=left] {[/align]
[align=left] cout << "is Example" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]typedef void (*py)();[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Base *b = new Example ;[/align]
[align=left] void* vptr = ( void *)*(unsigned long*)b;[/align]
[align=left] unsigned char *p = (unsigned char *)vptr;[/align]
[align=left] p += sizeof( void *) * 0;[/align]
[align=left] py vtable = ( py ) (void *)*(unsigned long *)p;[/align]
[align=left] vtable();[/align]
[align=left] p += sizeof( void *) * 1;[/align]
[align=left] py vtable2 = ( py ) (void *)*(unsigned long *)p;[/align]
[align=left] vtable2();[/align]
[align=left]}[/align]

[align=left]//return:[/align]
[align=left]// is Example[/align]
[align=left]// is Base say[/align]

三,动态绑定对象切割

(1). 切割

在某种情况下即便当前基类是一个指向派生类的引用(多态),也会将其转换为真正的基类,而派生类部分将被切掉。

(2). 值类型形参

当形参为值类型时,动态绑定类型将被切割,转换为纯基类类型。

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] virtual void ShowInfo()[/align]
[align=left] {[/align]
[align=left] cout << "is Base" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]class Example : virtual public Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] virtual void ShowInfo()[/align]
[align=left] {[/align]
[align=left] cout << "is Example" << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]void Test ( Base b )[/align]
[align=left]{[/align]
[align=left] b.ShowInfo();[/align]
[align=left]}[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Base *ex = new Example ;[/align]
[align=left] Test(*ex);[/align]
[align=left]}[/align]
[align=left]//return:[/align]
[align=left]//is Base[/align]

(3). 容器和数组

当将动态绑定的类型存入容器(对象而非指针)时,也会被切割,转换为纯基类类型。

[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Base *ex = new Example ;[/align]
[align=left] Base baseArray[3] = {};[/align]
[align=left] baseArray[0] = *ex;[/align]
[align=left] baseArray[0].ShowInfo();[/align]

[align=left] Base *ex2 = new Example ;[/align]
[align=left] Vector< Base > vr;[/align]
[align=left] vr.push_back(*ex2);[/align]
[align=left] vr[0].ShowInfo();[/align]
[align=left]}[/align]
[align=left]//return:[/align]
[align=left]// is Base[/align]
[align=left]// is Base[/align]

-

<原创文章 转载请注明出处 http://blog.csdn.net/meiwm 谢谢>

作者:meiwm

出处:http://blog.csdn.net/meiwm
本文为原创,本文版权归作者所有。欢迎转载,但请务必保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。

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