继承与派生
1.定义
继承 :
在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。
派生 :
派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
派生类一经定义后,可以独立使用,不依赖于基类。
派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public
在派生类的各个成员函数中,不能访问 基类中的private成员。
派生类写法:
1 2 3 4 class 派生类名:public 基类名{ };
实例:
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 #include <iostream> using namespace std;class Shape { public : void setWidth (int w) {width = w;} void setHeight (int h) {height = h;} protected : int width; int height; }; class PaintCost { public : int getCost (int area) {return area * 70 ;}}; class Rectangle : public Shape, public PaintCost{ public : int getArea () { return (width * height); }}; int main (void ) { Rectangle Rect; int area; Rect.setWidth (5 ); Rect.setHeight (7 ); area = Rect.getArea (); cout << "Total area: " << Rect.getArea () << endl; cout << "Total paint cost: $" << Rect.getCost (area) << endl; return 0 ; }
2. 派生类对象的内存空间
派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前 。
3.类的两种关系——继承关系与复合关系
继承:“是”关系。
基类 A,B是基类A的派生类。
逻辑上要求:“一个B对象也是一个A对象”。
复合:“有”关系。 (可能算下表中的组合关系?吧)
1 2 3 4 5 6 7 8 9 10 11 class CPoint { double x, y; friend class CCircle ; }; class CCircle { double r; CPoint center; };
复合关系的另一种写法:(可能算下表中的关联关系吧?)
例题:为“狗”类设一个“业主”类的对象指针;为“业主”类设一个“狗”类的对象指针数组。
1 2 3 4 5 6 7 8 9 10 class CMaster ; class CDog { CMaster *pm; }; class CMaster { CDog *dogs[10 ]; };
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 #include <iostream> using namespace std;class base { public : int j;int i; void func () {cout<<"fuck" <<endl;}; }; class derived : public base{ public : int i; void access () ; void func () {cout<<"fucku" <<endl;}; }; void derived::access () { j = 5 ; i = 5 ; base::i = 5 ; func (); base::func (); } int main () { derived obj; obj.i = 1 ; obj.base::i = 1 ; obj.access (); cout<<obj.j<<endl; return 0 ; }
输出结果:
注:若在基类中加入int k;并且在access方法中加入k=3;则报错,因为派生类的成员函数无权访问基类的私有变量。
4. 类的保护成员
基类的private成员:可以被下列函数访问
基类的public成员:可以被下列函数访问
基类的成员函数
基类的友元函数
派生类的成员函数
派生类的友元函数
其他的函数
基类的protected成员:可以被下列函数访问
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 #include <iostream> using namespace std;class Father { private : int nPrivate; public : int nPublic; protected : int nProtected; }; class Son : public Father{ void AccessFather () { nPublic = 1 ; nPrivate = 1 ; nProtected = 1 ; Son f; f.nProtected = 1 ; } }; int main () { Father f; Son s; f.nPublic = 1 ; s.nPublic = 1 ; f.nProtected = 1 ; f.nPrivate = 1 ; s.nProtected = 1 ; s.nPrivate = 1 ; return 0 ; }
5. 派生类的构造函数
例如,我们写了以下 “昆虫”类 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Bug { private : int nLegs; int nColor; public : int nType; Bug (int legs, int color); void PrintBug () {}; }; class FlyBug : public Bug { int nWings; public : FlyBug (int legs, int color, int wings); };
对于类FlyBug而言,只能使用 初始化列表构造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Bug::Bug (int legs, int color) { nLegs = legs; nColor = color; } FlyBug::FlyBug (int legs, int color, int wings) { nLegs = legs; nColor = color; nType = 1 ; nWings = wings; } FlyBug::FlyBug (int legs, int color, int wings) : Bug (legs, color) { nWings = wings; }
使用时,即初始化对象时写法:FlyBug fb ( 2,3,4);
注意:
在创建派生类的对象时,需要调用基类的构造函数 :初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数之前,总是先执行基类的构造函数 。
调用基类构造函数的两种方式
显式方式:在派生类的构造函数中,为基类的构造函数提供参数.
derived::derived(arg_derived-list):base(arg_base-list)
隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的无参构造函数
派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的析构函数。
考虑以下程序:
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 #include <iostream> using namespace std;class Base { public : int n; Base (int i) : n (i) {cout << "Base " << n << " constructed" << endl;} ~Base () {cout << "Base " << n << " destructed" << endl;} }; class Derived : public Base{ public : Derived (int i) : Base (i) {cout << "Derived constructed" << endl;} ~Derived () {cout << "Derived destructed" << endl;} }; int main () { Derived Obj (3 ) ; return 0 ; }
输出结果:
1 2 3 4 Base 3 constructed Derived constructed Derived destructed Base 3 destructed
6. 包含成员对象的派生类的构造函数写法
即:第一类复合关系的派生函数的构造函数的写法(关键在最后一行啊前面是铺垫)
也是使用初始化列表:
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 class Bug { private : int nLegs; int nColor; public : int nType; Bug (int legs, int color); void PrintBug () {}; }; class Skill { public : Skill (int n) {} }; class FlyBug : public Bug{ int nWings; Skill sk1, sk2; public : FlyBug (int legs, int color, int wings); }; FlyBug::FlyBug (int legs, int color, int wings) : Bug (legs, color), sk1 (5 ), sk2 (color), nWings (wings){}
7.封闭派生类对象的构造函数和析构函数的执行顺序
在创建派生类的对象时:
先执行基类的构造函数,用以初始化派生类对象中从基类继承的成员;
再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。
最后执行派生类自己的构造函数
在派生类对象消亡时:
先执行派生类自己的析构函数
再依次执行各成员对象类的析构函数
最后执行基类的析构函数
析构函数的调用顺序与构造函数的调用顺序相反。
8.public继承的赋值兼容规则
以public方式继承时,考虑以下程序段:
1 2 3 4 5 6 class base {}; class derived : public base{}; base b; derived d;
1) 派生类的对象可以赋值给基类对象
b = d;
2) 派生类对象可以初始化基类引用
base & br = d;
3) 派生类对象的地址可以赋值给基类指针
base * pb = & d;
如果派生方式是 private或protected,则上述三条不可行。
protected继承和private继承:
1 2 3 4 5 6 class base {}; class derived : protected base{}; base b; derived d;
protected继承时,基类的public成员和protected成员成为派生类的protected成员。
private继承时,基类的public成员成为派生类的private成员,基类的protected成员成为派生类的不可访问成员。
protected和private继承不是“是”的关系。
9. 基类与派生类的指针强制转换
公有派生的情况下,派生类对象的指针可以直接赋值给基类指针:
Base * ptrBase = &objDerived;
因为基类指针指向基类对象的首地址,也就是指向派生类对象,而派生类的所有变量地址中前几个是基类变量所拥有的地址,但是因为指针是指向基类的指针,所有只能访问的内存是基类的那几个变量。
ptrBase指向的是一个Derived类的对象;
*ptrBase可以看作一个Base类的对象,访问它的public成员直接通过ptrBase即可,但不能通过ptrBase访问objDerived对象中属于Derived类而不属于Base类的成员
即便基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类没有,而派生类中有的成员。
但是,通过强制指针类型转换,可以把ptrBase转换成Derived类的指针:
1 2 Base * ptrBase = &objDerived; Derived *ptrDerived = (Derived * ) ptrBase;
程序员要保证ptrBase指向的是一个Derived类的对象,否则很容易会出错。
10.直接基类和间接基类
在声明派生类时,只需要列出它的直接基类
派生类的成员包括:
派生类自己定义的成员
直接基类中的所有成员
所有间接基类的全部成员
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 Base { public : int n; Base (int i) : n (i) { cout << "Base " << n << " constructed" << endl; } ~Base () { cout << "Base " << n << " destructed" << endl; } }; class Derived : public Base{ public : Derived (int i) : Base (i) { cout << "Derived constructed" << endl; } ~Derived () { cout << "Derived destructed" << endl; } }; class MoreDerived : public Derived{ public : MoreDerived () : Derived (4 ) { cout << "More Derived constructed" << endl; } ~MoreDerived () { cout << "More Derived destructed" << endl; } }; int main () { MoreDerived Obj; return 0 ; }
输出结果:
1 2 3 4 5 6 Base 4 constructed Derived constructed More Derived constructed More Derived destructed Derived destructed Base 4 destructed
11. 多继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。
C++ 类可以从多个类继承成员,语法如下:
1 2 3 4 class <派生类名>:<继承方式1 ><基类名1 >,<继承方式2 ><基类名2 >,…{ <派生类类体> };
实例:
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 #include <iostream> using namespace std;class Shape { public : void setWidth (int w) { width = w; } void setHeight (int h) { height = h; } protected : int width; int height; }; class PaintCost { public : int getCost (int area) { return area * 70 ; } }; class Rectangle : public Shape, public PaintCost{ public : int getArea () { return (width * height); } }; int main (void ) { Rectangle Rect; int area; Rect.setWidth (5 ); Rect.setHeight (7 ); area = Rect.getArea (); cout << "Total area: " << Rect.getArea () << endl; cout << "Total paint cost: $" << Rect.getCost (area) << endl; return 0 ; }
输出结果:
1 2 Total area: 35 Total paint cost: $2450
附录:Python面向对象程序设计菜鸟教程链接Python 面向对象 | 菜鸟教程 (runoob.com)
Harbin Institute of Technology, Shenzhen 计算机科学与技术 本科在读