[TOC]
3.1 类与对象 对象 :用于描述客观事物的实体,用于构成系统的一个基本单位
类 :具有相同属性和行为的一组对象集合
类中的数据只能通过本类的方法进行处理 类通过一个简单的 外部接口
与外界发生关系 对象和类之间通过消息进行通信 抽象 :对一些具体问题进行概括,抽出一类可以描述这些问题的公共性质的过程
面向对象方法 :
将数据和对数据的操作封装在一起,作为一个相互依存、不可分离的整体——对象 对同类型的对象抽象出其共性形成类 特性:封装:将抽象得到的性质结合称为一个整体 继承与派生 多态性 1 2 3 4 5 6 class Name { ElemType data; ReturnType op () { return xx; } }
面向对象的语言 :程序模块是由类构成的
面向对象的软件开发
面向对象分析:OOA 面向对象设计:OOD 面向对象编程:OOP 面向对象测试:OOT 面向对象软件维护:OOSM 3.1.1 类成员的访问控制 public
:类外可访问
private
:只允许本类成员函数访问
protected
:在本类中具有 private
属性,在派生类中可以被访问,不可被外部使用者访问
3.1.2 类的成员函数 对象的访问方式 成员函数的实现 1 2 3 4 返回值类型 类名::函数成员名(参数表){ }
内联成员函数 隐式声明:将函数体放在类定义中;也可以使用 inline
3.1.3 构造函数 实现对象的初始化,定义对象时必须初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # include <iostream> using namespace std;class Clock {public : Clock (int newH,int newM,int newS); Clock (){ hour = 0 ; minute = 0 ; second = 0 ; } void setTime (int newH,int newM,int newS) ; void showTime () ; private : int hour,minute,second; }; Clock::Clock (int newH,int newM,int newS) { hour = newH; minute = newM; second = newS; } inline void Clock::showTime () { cout << hour << ":" << minute << ":" << second << endl; } int main () { Clock c2; Clock (1 ,2 ,3 ); c2.showTime (); return 0 ; }
复制构造函数 相同类型的类对象通过赋值构造函数完成复制过程
调用复制构造函数的三种情况 用类的一个对象 初始化另一对象
函数的实参与形参结合时
函数返回值是对象,赋值给调用者时
先赋值给临时对象,待执行完语句后,临时对象消失
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Point {public : Point (int xx,int yy):x (xx),y (yy){} Point (Point &p); int getX () {return x;} int getY () {return y;} private : int x,y; }; Point::Point (Point &p){ x = p.x; y = p.y; } Point middlePoint (Point &a,Point &b) { Point x ((a.getX()+b.getX())/2 ,(a.getY()+b.getY())/2 ) ; return x; } int main () { Point a (4 ,4 ) ; Point b (2 ,2 ) ; cout << middlePoint (a,b).getX () << endl; }
深复制与浅复制 复制:为新对象分配一块新空间,将原对象成员的值赋值给新对象成员
浅复制
在未自定义赋值构造函数时,会调用默认的复制构造函数,复制方式为 浅复制
简单的赋值拷贝:将变量对应的内存中的内容赋值给新变量对应的内存中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 # include <iostream> # include <cassert> using namespace std;class Point { public : Point ():x (0 ),y (0 ){ cout << "Default Constructor called" << endl; } Point (int xx,int yy):x (xx),y (yy){ cout << "Constructor called" << endl; } int getX () { return x; } int getY () { return y; } ~Point (){ cout << "Destructor called" << endl; } void move (int newX,int newY) { x = newX; y = newY; } private : int x,y; }; class ArrayOfPoints { public : ArrayOfPoints (int size):size (size){ points = new Point[size]; } ~ArrayOfPoints (){ delete [] points; } Point &element (int index) { assert (index >= 0 && index < size); return points[index]; } private : Point *points; int size; }; int main () { int count; cin >> count; ArrayOfPoints pointsArray1 (count) ; pointsArray1.element (0 ).move (5 ,10 ); pointsArray1.element (1 ).move (15 ,20 ); ArrayOfPoints pointsArray2 = pointsArray1; cout << "Copy of pointsArray1:" << endl; cout << "Point_0 of arrary2:" << pointsArray2.element (0 ).getX () << "," << pointsArray2.element (0 ).getY () << endl; cout << "Point_1 of arrary2:" << pointsArray2.element (1 ).getX () << "," << pointsArray2.element (1 ).getY () << endl; pointsArray1.element (0 ).move (25 ,30 ); pointsArray1.element (1 ).move (35 ,40 ); cout << "After the moving of pointsArray1:" << endl; cout << "Point_0 of arrary2:" << pointsArray2.element (0 ).getX () << "," << pointsArray2.element (0 ).getY () << endl; cout << "Point_1 of arrary2:" << pointsArray2.element (1 ).getX () << "," << pointsArray2.element (1 ).getY () << endl; return 0 ; }
由于浅复制是简单的赋值,对于指针成员变量,会将指针变量的值(即指向的内存地址)赋值给新对象的指针成员(出现两个指针变量指向同一个内存地址的情况)。
因此,调用析构函数时,对同一块内存单元会释放两次,所以会抛出异常
深复制
在对所有指针成员的对象进行复制时,必须自定义复制构造函数,在堆区重新开辟一块内存保存数据,才能避免内存被重复释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 # include <iostream> # include <cassert> using namespace std;class Point { public : Point ():x (0 ),y (0 ){ cout << "Default Constructor called" << endl; } Point (int xx,int yy):x (xx),y (yy){ cout << "Constructor called" << endl; } int getX () { return x; } int getY () { return y; } ~Point (){ cout << "Destructor called" << endl; } void move (int newX,int newY) { x = newX; y = newY; } private : int x,y; }; class ArrayOfPoints { public : ArrayOfPoints (int size):size (size){ points = new Point[size]; } ArrayOfPoints (const ArrayOfPoints &v); ~ArrayOfPoints (){ delete [] points; } Point &element (int index) { assert (index >= 0 && index < size); return points[index]; } private : Point *points; int size; }; ArrayOfPoints::ArrayOfPoints (const ArrayOfPoints &v){ size = v.size; points = new Point[size]; for (int i = 0 ;i < size;++i){ points[i] = v.points[i]; } } int main () { int count; cin >> count; ArrayOfPoints pointsArray1 (count) ; pointsArray1.element (0 ).move (5 ,10 ); pointsArray1.element (1 ).move (15 ,20 ); ArrayOfPoints pointsArray2 = pointsArray1; cout << "Copy of pointsArray1:" << endl; cout << "Point_0 of arrary2:" << pointsArray2.element (0 ).getX () << "," << pointsArray2.element (0 ).getY () << endl; cout << "Point_1 of arrary2:" << pointsArray2.element (1 ).getX () << "," << pointsArray2.element (1 ).getY () << endl; pointsArray1.element (0 ).move (25 ,30 ); pointsArray1.element (1 ).move (35 ,40 ); cout << "After the moving of pointsArray1:" << endl; cout << "Point_0 of arrary2:" << pointsArray2.element (0 ).getX () << "," << pointsArray2.element (0 ).getY () << endl; cout << "Point_1 of arrary2:" << pointsArray2.element (1 ).getX () << "," << pointsArray2.element (1 ).getY () << endl; return 0 ; } 2 Default Constructor called Default Constructor called Default Constructor called Default Constructor called Copy of pointsArray1: Point_0 of arrary2:5 ,10 Point_1 of arrary2:15 ,20 After the moving of pointsArray1: Point_0 of arrary2:5 ,10 Point_1 of arrary2:15 ,20 Destructor called Destructor called Destructor called Destructor called
析构函数 清理功能
析构函数在对象的生存期即将结束时被自动调用
3.1.4 类的组合 一个类可以嵌套其他类对象,包含与被包含关系
如果这个类具有内嵌对象成员,内嵌对象成员先被创建
存在多个内嵌对象成员时,构造函数调用顺序与其被声明次序有关,与初始化列表的顺序无关。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <cmath> using namespace std;class Point { public : Point (int xx = 0 ,int yy = 0 ):x (xx),y (yy){ cout << "Point Constructor:" << xx << endl; } Point (Point &p); int getX () {return x;} int getY () {return y;} private : int x,y; }; Point::Point (Point &p){ x = p.x; y = p.y; cout << "Point Copy Constructor x=" << x << endl; } class Line { public : Line (Point xp1,Point xp2); Line (Line &l); double getLen () { return len; } private : Point p1,p2; double len; }; Line::Line (Point xp1,Point xp2):p1 (xp1),p2 (xp2){ double x = static_cast <double >(p1.getX () - p2.getX ()); double y = static_cast <double >(p1.getY () - p2.getY ()); len = sqrt (x*x + y*y); cout << "Line Constructor" << endl; } Line::Line (Line &l):p1 (l.p1),p2 (l.p2){ len = l.len; cout << "Line Copy Constructor" << endl; } int main () { Point myp1 (0 ,0 ) ,myp2 (1 ,1 ) ; Line line1 (myp1,myp2) ; Line line2 (line1) ; cout << line1.getLen () << endl; cout << line2.getLen () << endl; return 0 ; }
前向引用声明 在被引用类未定义前,将被引用类的名称放在调用者前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class B ;class A { public : void f (B b) ; void method () ; private : B &b; }; void A::method () { b.b_method (); } class B { public : void b_method () ; }; void B::b_method () {}
3.1.5 结构体与类对比 结构体和类:
联合体:
全部数据成员共用同一内存单元
不能自定义构造函数,析构函数,重载赋值运算符
不能被继承,不支持多态 3.1.6 UML类图 在UML类图中,使用包含类名,属性 field
和方法 method
且有分割线的矩形表示类
属性表示 1 [访问控制] 属性名 [重数] : 类型 [默认值] [约束特征]
+
:public
-
:private
#
:protected
方法表示 1 [访问控制] 函数名 (参数列表):返回类型 [约束特征]
类关系表示 关联关系 单向关联
顾客(Customer)拥有地址(Address),则Customer类与Address类具有单向关联关系
多重性关联
一个界面(Form)可以拥有零个或多个按钮(Button),但是一个按钮只能属于一个界面
表示方法 多重性说明 1..1 本类对象与另一类对象之间是一对一关系 0..* 本类一个对象与另一类对象的零个或多个有关系 1..* 本类一个对象与另一类对象的一个或多个有关系 0..1 本类一个对象与另一类对象的零个或一个有关系 m..n 本类一个对象与另一类最少m个最多n个对象有关系
包含关系 聚合
聚合(Aggregation)关系表示整体与部分关系
在聚合关系中,成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立存在
汽车发动机(Engine)是汽车(Car)的组成部分,但是汽车发动机可以独立存在,因此,汽车和发动机是聚合关系
构成
构成(Composition)关系也表示类之间整体和部分的关系
但是在组合关系中整体对象可以控制成员对象的生命周期,一旦整体对象不存在,成员对象也将不存在
泛化 泛化(Generalization)关系也就是继承关系,用于描述父类与子类之间的关系
Student类和Teacher类都是Person类的子类,Student类和Teacher类继承了Person类的属性和方法,Person类的属性包含姓名(name)和年龄(age),每一个Student和Teacher也都具有这两个属性
另外Student类增加了属性学号(studentNo),Teacher类增加了属性教师编号(teacherNo),Person类的方法包括行走move()和说话say(),Student类和Teacher类继承了这两个方法,而且Student类还新增方法study(),Teacher类还新增方法teach()
注释
3.1.7 临时对象 1 2 3 4 5 6 7 8 cout << Line (Point (1 ),Point (4 )).getLen <<endl; cout << Line ((Point)1 ,(Point)4 ).getLen << endl; cout << Line (static_cast <Point>1 ,static_cast <Point>4 ) << endl; cout << Line (1 ,4 ).getLen << endl; explict Point (int xx = 0 ,yy = 0 ) :x(xx),y(yy){ }
3.2 继承与派生 继承:派生类通过继承得到基类的特性
用于解决复用问题
3.2.1 派生类定义及生成 派生类定义 1 2 3 4 5 6 7 8 9 class 派生类名:继承方式 基类1 ,继承方式 基类2 ,...{ }; class Derived :public Base1,private Base2{ public : Derived (); ~Derived (); };
派生类生成过程 吸收基类成员函数
改造基类
添加新成员
同名隐藏:若派生类声明了与基类同名的成员函数(不管参数列表是否相同)或成员属性,则隐藏基类同名成员
被隐藏的同名函数,仍存在。对象名.基类名:: func();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> using namespace std;class Point { public : Point (int xx = 0 ,int yy = 0 ):x (xx),y (yy){ cout << "Point Constructor:" << xx << endl; } Point (Point &p); int getX () {return x;} int getY () {return y;} private : int x,y; }; Point::Point (Point &p){ x = p.x; y = p.y; cout << "Point Copy Constructor x=" << x << endl; } class PPoint :public Point{ public : PPoint (int xx = 0 ,int yy = 0 ):x (xx),y (yy){ cout << "PPoint Constructor:" << xx << endl; } int getX () {return x+1 ;} int getY () {return y+1 ;} private : int x,y; }; int main () { PPoint pp (1 ,1 ) ; cout << pp.getX () << endl; return 0 ; }
继承方式 用于控制复用部分的访问权限
默认的继承方式是私有继承
类族中或类族外都无法访问基类的私有成员
公有继承 基类的公有成员和保护成员在派生类中访问属性不变
在类族之外只能通过派生类的对象访问从基类继承的公有成员,不可访问保护成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # include <iostream> using namespace std;class Base { public : Base (int a,int b,int c):basePublic (a),baseProtected (b),basePrivate (c){} int basePublic; protected : int baseProtected; private : int basePrivate; }; class PublicDerived :public Base{ public : PublicDerived (int a,int b,int c):Base (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; int main () { PublicDerived son1 (1 ,2 ,3 ) ; cout << son1.basePublic << endl; cout << son1.baseProtected << endl; cout << son1.basePrivate << endl; }
私有继承 公有成员和保护成员都以私有成员身份出现在派生类中,不可再向下派生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # include <iostream> using namespace std;class Base { public : Base (int a,int b,int c):basePublic (a),baseProtected (b),basePrivate (c){} int basePublic; protected : int baseProtected; private : int basePrivate; }; class ProtectedDerived :protected Base{ public : ProtectedDerived (int a,int b,int c):Base (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; int main () { ProtectedDerived son1 (1 ,2 ,3 ) ; cout << son1.basePublic << endl; cout << son1.baseProtected << endl; cout << son1.basePrivate << endl; }
保护继承 基类的公有成员和保护成员以保护成员身份出现在派生类中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 # include <iostream> using namespace std;class Base { public : Base (int a,int b,int c):basePublic (a),baseProtected (b),basePrivate (c){} int basePublic; protected : int baseProtected; private : int basePrivate; }; class ProtectedDerived :protected Base{ public : ProtectedDerived (int a,int b,int c):Base (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; class DoubleProtectedDerived :protected ProtectedDerived{ public : DoubleProtectedDerived (int a,int b,int c):ProtectedDerived (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; int main () { ProtectedDerived son1 (1 ,2 ,3 ) ; cout << son1.basePublic << endl; cout << son1.baseProtected << endl; cout << son1.basePrivate << endl; }
在孙子代,私有继承无法访问间接继承基类,保护继承可以间接继承基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 # include <iostream> using namespace std;class Base { public : Base (int a,int b,int c):basePublic (a),baseProtected (b),basePrivate (c){} int basePublic; protected : int baseProtected; private : int basePrivate; }; class ProtectedDerived :protected Base{ public : ProtectedDerived (int a,int b,int c):Base (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; class PrivateDerived :private Base{ public : PrivateDerived (int a,int b,int c):Base (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; class PublicProtectedDerived :public ProtectedDerived{ public : PublicProtectedDerived (int a,int b,int c):ProtectedDerived (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; class PublicPrivateDerived :public PrivateDerived{ public : PublicPrivateDerived (int a,int b,int c):PrivateDerived (a,b,c){ cout << basePublic << endl; cout << baseProtected << endl; cout << basePrivate << endl; } }; int main () { ProtectedDerived son1 (1 ,2 ,3 ) ; cout << son1.basePublic << endl; cout << son1.baseProtected << endl; cout << son1.basePrivate << endl; }
3.2.2 派生类的构造函数和析构函数 派生类的构造函数只负责对新增成员初始化,基类成员的初始化由基类构造函数完成
派生类构造函数 1 2 3 派生类名::派生类名(参数表)::基类名1 (基类1 初始化参数列表),...基类名n (基类n初始化参数列表),派生类新增成员对象(派生类新增成员对象参数列表){ };
执行顺序
调用基类的构造函数,调用顺序按他们在被继承时声明的顺序 对派生类新增成员对象初始化,调用顺序按其在类中的声明顺序 执行派生类的构造函数体中内容对于构造函数中未显示列出的基类,系统会调用默认构造函数 复制构造函数 1 2 Derived::Derived (Derived &v):Base (v){}
析构函数 派生类的析构函数只需要负责清理新增的成员,系统会自动调用基类的析构函数
析构函数调用顺序
执行析构函数的函数体 对派生类新增的成员进行清理 对所有从基类继承来的成员清理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 # include <iostream> using namespace std;class Base1 { public : Base1 (int i){ cout << "Constructing Base1 " << i << endl; } ~Base1 (){ cout << "Destructing Base1 " << endl; } }; class Base2 { public : Base2 (int j){ cout << "Constructing Base2 " << j << endl; } ~Base2 (){ cout << "Destructing Base2" << endl; } }; class Base3 { public : Base3 (){ cout << "Constructing Base3 * " << endl; } ~Base3 (){ cout << "Destructing Base3 " << endl; } }; class Derived :public Base2,public Base1,public Base3{ public : Derived (int a,int b,int c,int d):Base1 (a),member2 (d),member1 (c),Base2 (b){} private : Base1 member1; Base2 member2; Base3 member3; }; int main () { Derived obj (1 ,2 ,3 ,4 ) ; return 0 ; } Constructing Base2 2 Constructing Base1 1 Constructing Base3 * Constructing Base1 3 Constructing Base2 4 Constructing Base3 * Destructing Base3 Destructing Base2 Destructing Base1 Destructing Base3 Destructing Base1 Destructing Base2
3.2.3 类型(向上)兼容 在任何需要基类对象的地方 都可以使用派生类的对象来代替——相当于隐式类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # include <iostream> using namespace std;class Base1 { public : void display () { cout << "Base1::display" <<endl; } }; class Base2 :public Base1{ public : void display () { cout << "Base2::display" <<endl; } }; class Derived :public Base2{ public : void display () { cout << "Derived::display" <<endl; } }; void fun (Base1 *ptr) { ptr->display (); } int main () { Base1 base1; Base2 base2; Derived derived; fun (&base1); fun (&base2); fun (&derived); return 0 ; } Base1::display Base1::display Base1::display
3.2.4 派生类成员的表示与控制 作用域分辨符::
同名隐藏 :如果派生类中声明了与基类同名的新函数,即使参数列表不同,从基类继承的同名函数的所有重载形式都会被隐藏
using关键字 解决同名隐藏问题
using
的作用是将一个作用域名引入到另一个作用域中
将 using
用于基类中的函数名,派生类中定义同名但参数列表不同的函数,派生类也不会发生同名隐藏
1 2 3 4 5 6 class Derived :public Base{ public : using Base::fun; void fun (int i) {} };
即多个路径上的派生类对同一基类处理,使用 using ::
会形成多个基类副本 ——不一定是一件好事,通过虚基类解决 3.2.5 虚基类 将共同基类设为虚基类
通过继承同一虚基类的所有派生类,访问基类的数据与函数成员时,会访问同一份映射 虚基类关键字作用范围和继承方式只对紧随其后的基类起作用
1 class 派生类名:virtual 继承方式 基类名{};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 # include <iostream> using namespace std;class Base0 { public : int var0; void fun0 () { cout << "Member of Base0" << endl; } }; class Base1 :virtual public Base0{ public : int var1; void fun0 () { cout << "Member of Base1" << endl; } }; class Base2 :virtual public Base0{ public : int var2; void fun0 () { cout << "Member of Base2" << endl; } }; class Derived :public Base1,public Base2{ public : int var; using Base0::fun0; void fun0 () { cout << "Member of Derived" << endl; } }; int main () { Derived d; d.var0 = 2 ; cout << d.var0 << endl; d.fun0 (); return 0 ; } 2 Member of Derived
虚基类及派生类的构造函数 只保证了虚基类只有一份成员数据,并不消除类型兼容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <iostream> using namespace std;class Furniture { public : Furniture (){}; Furniture (double weight); void show (void ) ; protected : double weight; }; Furniture::Furniture (double weight){ this ->weight = weight; } void Furniture::show () { cout << "家具重量为:" << weight << endl; } class Sofa :virtual public Furniture{ public : Sofa (){}; Sofa (double weight); void show (void ) ; }; Sofa::Sofa (double weight):Furniture (weight){ this ->weight = weight; } void Sofa::show () { cout << "沙发重量为:" << weight << endl; } class Bed :virtual public Furniture{ public : Bed (){}; Bed (double weight); void show (void ) ; }; Bed::Bed (double weight):Furniture (weight) this ->weight = weight; } void Bed::show () { cout << "床重量为:" << weight << endl; } class SofaBed :public Sofa,public Bed{ public : SofaBed (){}; SofaBed (double weight); void show (void ) ; }; SofaBed::SofaBed (double weight) :Furniture (weight),Sofa (weight), Bed (weight){ this ->weight = weight; } void SofaBed::show () { cout << "沙发床重量为:" << weight << endl; } int main () { Furniture furniture (1 ) ; Sofa sofa (2 ) ; Bed bed (3 ) ; SofaBed sofabed (3.5 ) ; sofabed.show (); Furniture *p; p = &furniture; p->show (); p = &sofa; p->show (); p = &bed; p->show (); system ("pause" ); return 0 ; }
3.2.6 对象的构造顺序 该类有直接或间接的虚基类,则先执行虚基类的构造函数 按照基类在派生类中的声明顺序执行构造函数 按照声明顺序对新增成员初始化 执行构造函数的函数体 3.3 多态性 指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
3.3.1 多态及其实现 多态分类 专用多态
重载多态:普通函数和成员函数,运算符重载 强制多态:变量的类型变化 通用多态
包含多态:同一类族中,不同类的同名函数的多态行为 参数多态 编译时多态:函数重载,运算符重载 运行时多态:虚函数机制 绑定:计算机程序自身相互关联的过程:一条消息和一个对象方法相结合的过程
静态绑定:发生在编译期间 动态绑定:发生在程序运行期间,根据在执行期间判断所引用对象的实际类型调用相应的方法 运行时多态 运行期多态的实现依赖虚函数机制。当某个类声明了虚函数时,编译器将为该类对象声明一个虚函数表指针,并为该类设置一张唯一的虚函数表,虚函数表中存放的是该类虚函数的地址。运行期间通过虚函数表指针与虚函数表确定该类虚函数的真正实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Animal { public : virtual void shout () = 0 ; }; class Dog :public Animal{ public : virtual void shout () { cout << "汪汪!" <<endl; } }; class Cat :public Animal{ public : virtual void shout () { cout << "喵喵~" <<endl; } }; class Bird : public Animal{ public : virtual void shout () { cout << "叽喳!" <<endl; } }; int main () { Animal *anim1 = new Dog; Animal *anim2 = new Cat; Animal *anim3 = new Bird; anim1->shout (); anim2->shout (); anim3->shout (); ... return 0 ; }
3.3.2 运算符多态(编译时多态) 与函数重载一样,都是通过在编译时静态绑定
限制:
只能重载C++已有的运算符 不能改变运算符操作数数量,至少有一个自定义类型 重载之后,优先级和结合性不变 不能用于重载的运算符:类属性访问符 .
指针访问符 ->
, *
作用域 ::
三目运算符 ?:
1 2 3 返回类型 operator 运算符(形参表){ }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # include <iostream> using namespace std;class Complex { public : Complex (double r = 0.0 ,double i = 0.0 ):real (r),imag (i){} friend Complex operator +(const Complex &c1,const Complex &c2); friend Complex operator -(const Complex &c1,const Complex &c2); friend ostream & operator << (ostream &out,const Complex &c); private : double real; double imag; }; Complex operator + (const Complex &c1,const Complex &c2){ return Complex (c1.real + c2.real,c1.imag + c2.imag); } Complex operator - (const Complex &c1,const Complex &c2){ return Complex (c1.real - c2.real,c1.imag - c2.imag); } ostream & operator << (ostream &out,const Complex &c){ out << "(" << c.real << "," << c.imag << ")" ; return out; } int main () { Complex c1 (5 ,4 ) ,c2 (2 ,10 ) ,c3 ; cout << "c1 = " << c1 << endl; cout << "c2 = " << c2 << endl; c3 = c1 - c2; cout << "c3 = c1 - c2 = " << c3 << endl; c3 = c1 + c2; cout << "c3 = c1 + c2" << c3 << endl; return 0 ; }
类的运算符重载成员函数 运算符重载形参表会少一个,第一个操作数为调用该函数的对象,函数体可以通过 this
指针访问调用对象
双目运算符左操作数为调用函数对象本身的数据,由 this
指出 若为单目运算符,则为调用对象本身,不需要传递参数 单目运算符重载 前置单目运算符
1 2 3 4 5 6 7 8 9 10 class A { A& operator 运算符(){ } }; A oprand; ++operand;
后置单目运算符
1 2 3 4 5 6 7 8 9 10 11 12 class A { A operator 运算符(int ){ } }; A operand; operand++; operand--;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 # include <iostream> using namespace std;class Clock { public : Clock (int hour = 0 ,int minute = 0 ,int second = 0 ); void showTime () ; Clock& operator ++(); Clock operator ++(int ); private : int hour,minute,second; }; Clock::Clock (int hour,int minute,int second){ if (0 < hour && hour <= 24 && 0 < minute && minute <= 60 && 0 < second && second <= 60 ){ this ->hour = hour; this ->minute = minute; this ->second = second; } else { cout << "Time Error !!" << endl; } } void Clock::showTime () { cout << hour << ":" << minute << ":" << second << endl; } Clock& Clock::operator ++(){ second++; if (second >= 60 ){ second -= 60 ; minute++; if (minute >= 60 ){ minute -= 60 ; hour = (hour + 1 ) % 24 ; } } return *this ; } Clock Clock::operator ++(int ){ Clock old = *this ; ++(*this ); return old; } int main () { Clock myClock (23 ,59 ,59 ) ; cout << "First time:" ; myClock.showTime (); cout << "Show myTime++:" ; (myClock++).showTime (); cout << "++Show myTime:" ; (++myClock).showTime (); return 0 ; } First time:23 :59 :59 Show myTime++:23 :59 :59 ++Show myTime:0 :0 :1
双目运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 class A { A operator 运算符(DataType B){ } }; class C {};A operand1; C operand2; operand1 运算符 operand2;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 # include <iostream> using namespace std;class Complex { public : Complex (double r = 0.0 ,double i = 0.0 ):real (r),imag (i){} Complex operator +(const Complex &c2)const ; Complex operator -(const Complex &c2)const ; void display () const ; private : double real,imag; }; Complex Complex::operator +(const Complex &c2) const { return Complex (real + c2.real,imag + c2.imag); } Complex Complex::operator -(const Complex &c2) const { return Complex (real - c2.real,imag - c2.imag); } void Complex::display () const { cout << "(" << real << "," << imag << ")" << endl; } int main () { Complex c1 (5 ,4 ) ,c2 (2 ,10 ) ,c3 ; cout << "c1 = " ;c1.display (); cout << "c2 = " ;c2.display (); c3 = c1 - c2; cout << "c3 = c1 - c2 = " ;c3.display (); c3 = c1 + c2; cout << "c3 = c1 + c2 = " ;c3.display (); return 0 ; } c1 = (5 ,4 ) c2 = (2 ,10 ) c3 = c1 - c2 = (3 ,-6 ) c3 = c1 + c2 = (7 ,14 )
3.3.3 虚函数(运行时多态) 虚函数不能是构造函数,不能是静态的成员函数
带默认值的形参函数,不能在派生类中重新定义,属于静态绑定
除声明为内联函数的虚函数,其余虚函数都是动态绑定的
如何确定派生类的成员函数为虚函数 :相同函数名、相同形参列表、返回值一致的成员函数确认为虚函数
基类含有的虚函数,其派生类必须覆盖基类虚函数及所有同名重载函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <iostream> using namespace std;class Furniture { public : Furniture (){}; Furniture (double weight); virtual void show (void ) ; protected : double weight; }; Furniture::Furniture (double weight){ this ->weight = weight; } void Furniture::show () { cout << "家具重量为:" << weight << endl; } class Sofa :virtual public Furniture{ public : Sofa (){}; Sofa (double weight); void show (void ) ; }; Sofa::Sofa (double weight):Furniture (weight){ this ->weight = weight; } void Sofa::show () { cout << "沙发重量为:" << weight << endl; } class Bed :virtual public Furniture{ public : Bed (){}; Bed (double weight); void show (void ) ; }; Bed::Bed (double weight):Furniture (weight){ this ->weight = weight; } void Bed::show () { cout << "床重量为:" << weight << endl; } class SofaBed :public Sofa,public Bed{ public : SofaBed (){}; SofaBed (double weight); void show (void ) ; }; SofaBed::SofaBed (double weight) :Furniture (weight),Sofa (weight), Bed (weight){ this ->weight = weight; } void SofaBed::show () { cout << "沙发床重量为:" << weight << endl; } int main () { Furniture furniture (1 ) ; Sofa sofa (2 ) ; Bed bed (3 ) ; SofaBed sofabed (3.5 ) ; sofabed.show (); Furniture *p; p = &furniture; p->show (); p = &sofa; p->show (); p = &bed; p->show (); system ("pause" ); return 0 ; } 沙发床重量为:3.5 家具重量为:1 沙发重量为:2 床重量为:3 请按任意键继续. . .
可以用 基类名::函数名()
调用基类被隐藏的函数
基类构造函数 Base::Base()
中调用 virtual
修饰的虚函数,被调用的是基类 Base::
中的虚函数
析构函数类似
虚析构函数 不能声明构造函数,但可以声明虚析构函数
适用情况: 如果有可能通过基类指针调用对象的析构函数,就需要让基类的析构函数为虚函数
3.3.4 抽象类(运行时多态) 纯虚函数 1 virtual 函数类型 函数名(参数表) = 0 ;
声明为纯虚函数后,不能给出函数定义部分,带纯虚函数的类不能进行实例化
对基类纯虚函数的调用,必须通过 基类名::函数名(参数列表)
抽象类 带纯虚函数的类为抽象类
抽象类不能被实例化,但可以使用指针、引用
若其派生类给出纯虚函数的定义,不再是抽象类,可以进行实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 # include <iostream> using namespace std;class Base1 { public : virtual void display () const = 0 ; }; class Base2 :public Base1{ public : void display () const ; }; void Base2::display () const { cout << "Base2::display" <<endl; } class Derived :public Base2{ public : void display () const ; }; void Derived::display () const { cout << "Derived::display" <<endl; } void fun (Base1 *ptr) { ptr->display (); } int main () { Base2 base2; Derived derived; fun (&base2); fun (&derived); return 0 ; } Base2::display Derived::display
3.3.5 多态性总结 动态类型与非动态类型声明原则 如果一个函数的执行方式十分明确,则将他声明为非虚函数;
如果一个类族中所有类都有这个函数特点,则将这个函数声明为虚函数;
如果一个类需要被其他类对其进行继承,就需要设计为多态,至少设计为虚析构函数
多态的三个条件 继承:多态性体现在基类与派生类的继承关系中 重写:父类中定义方法,子类中有对方法的重写 类型兼容:父类的引用指向子类的对象 多态的实现——重写 重写与重载 前提:方法名一致
重载
发生在同一类中,
方法名相同
参数列表不同:参数个数,类型,顺序
参数类型不同时,顺序不同也可以理解为参数列表不同 参数类型相同时,顺序不同不构成重载 重载与返回类型无关
重写
一定发生在父子类之间,方法名相同
参数列表相同(参数个数,类型,顺序)
子类返回值类型和抛出的异常要是父类的子类
子类的访问控制权限比父类更高或相等