第十二章 继承
1. 什么是继承和派生?
继承:通常把子类不加修改的延续父类的特征,我们把它叫做继承
派生:子类在延续父类特征的基础上有添加了自己的新特征(这个过程叫做类的派生)
基类(父类),派生类(子类)
2. 复杂的继承和派生.
3. 继承和派生如何在C++中实现.
class son:public father
4. 单一继承.
在实际应用中可以将 基类和派生类视为一个整体
5. 保护成员.
类的私有成员只能被该类的公有成员和友元所访问
1 //友元函数 2 #include "iostream" 3 using namespace std; 4 5 class Point 6 { 7 public: 8 Point(double xx, double yy) { x=xx; y=yy; } 9 void Getxy(); 10 friend double Distance(Point &a, Point &b);//友元函数在类中声明 11 private: 12 double x, y; 13 }; 14 15 void Point::Getxy() 16 { 17 cout<<"("<<","<<")"<<endl; 18 } 19 //计算两点之间的距离 20 double Distance(Point &a, Point &b)//不同于类成员函数,友元函数无需类的属性 21 { 22 double dx = a.x - b.x;//可以直接调用类的私有成员 23 double dy = a.y - b.y; 24 return sqrt(dx*dx+dy*dy); 25 }
1 //友元类 友元类的所有成员函数都是另一个类的友元函数 2 #include"iostream" 3 using namespace std; 4 //class Girl; 5 class Girl 6 { 7 private: 8 char *name; 9 int age; 10 friend class Boy;//声明位置:公有私有均可,常写为私有(把类看成一个变量) 11 public: 12 Girl(int x) 13 { 14 age = x; name = new char[6];//也可以直接放到栈中 name = "Smili"; 15 //memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。 16 //memset(name,0,6);不这样做就会出错//int a[5];memset(a,1,5);//这里错了改成memset(a,1,5 *sizeof(int))就可以了最后一个参数是字节,而char只有一个字节 17 //memcpy(name,"Smili",5); 18 strcpy(name,"Smili");//拷贝了6个字符 19 }//这里怎么加全名Smili??? 20 ~Girl(){delete []name;name = NULL;} 21 }; 22 class Boy 23 { 24 public: 25 void disp(Girl &); 26 }; 27 void Boy::disp(Girl &r){cout<<"girl's name is:"<<r.name<<",age:"<<r.age<<endl;}//此处体现了友元类的作用 28 int main() 29 { 30 Girl Smili(18); 31 Boy Joy; 32 Joy.disp(Smili); 33 return 0; 34 }
private体现了封装的含义:
派生类的成员不能直接访问基类的私有成员,目的是为了防止私有成员被其他类调用时修改数据;
为了解决这个问题我们引进了protected。
派生类可以访问基类的protected成员(前提:公有派生)
6. 公有派生的公有成员.
子类可以直接调用
7. 公有派生的保护成员.
子类可以通过自己间接访问
1 #include <iostream> 2 using namespace std; 3 class father 4 { 5 protected: 6 void room(){cout<<"父亲的大房子我可以享受 ";} 7 }; 8 class son:public father 9 { 10 public: 11 void enjoy(){room();} 12 }; 13 int main() 14 { 15 son a; 16 //a.room(); 17 a.enjoy(); 18 return 0; 19 }
8. 公有派生的私有成员.
子类只能通过父类间接访问
public:门开的人可以直接进,protected/private:门是锁的钥匙在自家类里,不可直接访问:门是锁的钥匙在别人类里。
9. 继承的赋值.
只能小的调用大的,父类调用子类
到底是类型转换还是赋值??????????????????
见程序12-3-1
10. 私有派生.
11. 多重继承.
class son:public father,private mother
12. 继承的构造与析构.
//单一继承的构造顺序:先父类后子类
class f:public e,public b,public c,public d,public a
//多重继承的构造顺序:e,b,c,d,a,f
13. 向基类构造函数传递参数.
1 //在创建派生类对象时,对数据初始化 2 #include<iostream> 3 using namespace std; 4 class father 5 { 6 public: 7 father(int i){a=i;cout<<"构造基类a的值:"<<a<<endl;} 8 ~father(){cout<<"析构基类a的值:"<<a<<endl;} 9 private: 10 int a; 11 12 }; 13 class son:public father 14 { 15 public: 16 son(int i,int j,int k); 17 ~son(){cout<<"析构子类b的值:"<<b<<endl;} 18 private: 19 int b; 20 father age; 21 22 }; 23 //将父类放在花括号外定义,子类成员放在花括号内定义,增加代码的可读性 24 son::son(int i,int j,int k):father(i),age(j)//利用派生类构造函数初始化基类的构造函数 25 { 26 //age=j;出错要调用无参构造函数 27 b=k; 28 cout<<"构造子类b的值:"<<b<<endl; 29 } 30 void main() 31 { 32 son b(1,2,3); 33 }
14. 多重继承容易产生两义性.
产生原因: 当多个基类中有相同的函数, 用子类定义的类调用该函数时
解决方法:①子类中添加同名函数②添加作用域限定符Tom.father::human();③单一继承④虚基类virtual
9. 继承中的重载.
子类会屏蔽掉父类的同名函数,不论参数是否不同.
此时就要引用virtual。
本章总结:
1. 继承:***********************①
派生方式 基类的访问限定 派生类中对基类成员的访问限定 主函数中访问派生类对象的基类成员
共有派生 public public 可以直接访问
protected protected 不可直接访问
private 不可直接访问 不可直接访问
私有派生 public private 不可直接访问
protected private 不可直接访问
private 不可直接访问 不可直接访问
public:门开的人可以直接进,protected/private:门是锁的钥匙在自家类里,不可直接访问:门是锁的钥匙在别人类里。
2. 继承的赋值:只能小的调用大的,父类调用子类(虚函数处有用,体现多态性)
Father A; A=B √
Father *f; B=A × //B.operator=(B &r){}//r用A传,A填不满B
Son B; f=&B √
Son *s; S=&A × //S的范围大,要是移动的话,就超出A对象了。就会出错
3. 解决两义性的方法:
①子类中添加同名函数
②加作用域限定符class Son:class father,class mother{}; Son XiaoMing; XiaoMing.father::x();
③单一继承
④虚基类virtual
class human{public: stand(){cout<<”人可以直立行走 ”;}};
class father:virtual public human{public};
class mother:virtual public human{public};
class son:public father,pblic mother{public};
son XiaoMing;
XiaoMing.stand();
4. 继承的构造与析构
①单一继承的构造顺序:先父类后子类。(父类的构造函数,不论有无调用都要构造一遍)
②class f:public e,public b, public c
多重继承的构造顺序:e,b,c,f
5. 基类的构造函数,一般由派生类传递初始值。
派生类成员一般在{}内定义,用于增加代码的可读性。
Son::Son(int x, int y, int z, int t):Fobject(x,y){c=z;d=t;}
7.
重载 | 覆盖 | 隐藏 | |
共同点: | 函数名相同 | 函数名相同 | 函数名相同 |
不同点: |
同类、参数类型、数量不同 或 有无const |
不同类,同参,有基virtual |
不同类,同参,且无virtual 不同类,不同参(不论有无virtual) |
体现: | 由函数调用(静态联编) | 由函数调用取决于object(动态联编) | 取决于pointer(不能体现多态性) |
1 /** 2 1.继承:***********************① 3 派生方式 基类的访问限定 派生类中对基类成员的访问限定 主函数中访问派生类对象的基类成员 4 共有派生 public public 可以直接访问 5 protected protected 不可直接访问 6 private 不可直接访问 不可直接访问 7 私有派生 public private 不可直接访问 8 protected private 不可直接访问 9 private 不可直接访问 不可直接访问 10 public门开的人可以直接进,protected/private门是锁的钥匙在自家类里,不可直接访问:门是锁的钥匙在别人类里。 11 12 2.继承的赋值:只能小的调用大的,父类调用子类(虚函数处有用) 13 3.子类会屏蔽掉父类的同名重载函数(见word文档)**********************② 14 4.解决两义性的方法:①子类中添加同名函数②添加作用域限定符③单一继承④虚基类virtual(见word文档)**********************③ 15 */ 16 // 17 #include"iostream" 18 using namespace std; 19 class father 20 { 21 public: 22 father(){cout<<"father的无参构造 ";} 23 father(int m,int n){a=m;pa=n;cout<<"father的有参构造"<<a<<" "<<pa<<endl;} 24 ~father(){cout<<"father的析构 ";} 25 protected: 26 int pa; 27 private: 28 int a; 29 }; 30 31 class son:public father//class son:virtual public father 虚基类 32 { 33 public: 34 son(){cout<<"son的无参构造 ";} 35 son(int x,int y,int z,int t); 36 //派生类中初始化基类构造函数(有优势) 37 //不初始化就会调用father的无参构造函数 38 ~son(){cout<<"son的析构 ";} 39 protected: 40 int pb; 41 father Fobject; 42 private: 43 int b 44 }; 45 son::son(int x,int y,int z,int t):Fobject(x,y) 46 {pb=t;b=z;cout<<"son的有参构造"<<b<<" "<<pb<<endl;} 47 int main() 48 { 49 son Sobject(3,4,5,6);//为什么这里要调用无参的构造函数? 50 return 0; 51 }