1.继承和派生
继承的概念:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类 ,而把B作为基类的一个派生类 (也称为子类)。
派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
派生类一经定义后,可以独立使用,不依赖与基类。
派生类拥有基类的全部成员变量和成员函数 ,包括private
,public
,protected
派生类的写法:class 派生类名: public 基类名 { };
派生类的内存空间 :派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。
1 2 3 4 5 6 class CBase { int v1, v2; }; class CDerived :public CBase{ int v3; }
示例 :写了一个学生的类Student
,再写一个Student类的派生类UndergraduateStudent
,补充和修改一些功能。
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 #include <iostream> #include <string> using namespace std ;class Student { private : string name; string id; char gender; int age; public : void PrintInfo () { cout << "Name: " <<name<< endl ; cout << "ID: " << id << endl ; cout << "Age: " << age << endl ; cout << "Gender: " << gender << endl ; } void SetInfo (const string &_name, const string &_id, int _age, char _gender) { name = _name; id = _id; age = _age; gender = _gender; } string GetName () { return name; } }; class UndergraduateStudent :public Student { private : string department; public : void QualifiedForBaoyan () { cout << "qualified for baoyan" << endl ; } void PrintInfo () { Student::PrintInfo(); cout << "Department:" << department << endl ; } void SetInfo (const string &_name, const string &_id, int _age, char _gender, const string &_department) { Student::SetInfo(_name, _id, _age, _gender); department = _department; } }; int main () { UndergraduateStudent S; S.SetInfo("Harry Potter" , "20200217" , 19 , 'M' , "Machine Learning" ); cout << S.GetName() << " " ; S.QualifiedForBaoyan(); S.PrintInfo(); return 0 ; }
程序运行结果:
2.继承关系和复合关系 类与类之间有两种关系:
继承 :“是”关系
基类A,B是基类A的派生类
逻辑上要求:“一个B对象也是一个A对象”,比如上节中Student
类和UndergraduateStudent
类。
复合 :“有”关系
类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。
举一个简单的例子:如果要写一个小区养狗管理程序,需要写一个“业主”类和“狗”类,狗的主人即是业主,规定狗只能有一个主人,而一个业主最多可以有5条狗。
正确的写法:为”狗“类设一个”业主“类的对象指针;为”业主“类设一个”狗“类的对象指针数组。
1 2 3 4 5 6 7 class Master ;class Dog { Master *m; }; class Master { Dog dogs[10 ]; };
而以下的做法都是错误的或者不好的:
1 2 3 4 5 6 7 class Dog ;class Master { Dog dogs[10 ]; }; class Dog { Master m; };
1 2 3 4 5 6 7 class Dog ;class Master { Dog *dogs[10 ]; }; class Dog { Master m; };
1 2 3 4 5 6 7 class Master ;class Dog { Master *m; }; class Master { Dog 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 class base { int j; public : int i; void func () ; }; class derived :public base{public : int i; void access () ; void func () ; }; void derived::access(){ j = 5 ; i = 5 ; base::i = 5 ; func(); base::func(); } int main () { derived obj; obj.i = 3 ; obj.base::i = 3 ; }
obj
对象占用的存储空间:
注意:一般来说,基类和派生类不定义同名成员变量 。
4.访问范围说明符
基类的private
成员,可以被下列函数访问:
基类的public
成员,可以被下列函数访问:
基类的成员函数
基类的友元函数
派生类的成员函数
派生类的友元函数
其他的函数
基类的protected
成员,可以被下列函数访问:
基类的成员函数
基类的友元函数
派生类的成员函数可以访问当前对象的基本的 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 class Father {private : int nPrivate;public : int nPublic;protected :int nProtected;}; class Son :public Father{ void AccseeFather () { 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.nPublic = 1 ; s.nProtected = 1 ; s.nPublic = 1 ; return 0 ; }
5.派生类的构造函数
派生类对象包含基类对象
执行派生类构造函数之前,先执行基类的构造函数
派生类交代基类初始化,具体形式:
1 2 3 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 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); }; 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; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 ; }
程序运行结果为:
6.public继承的赋值兼容规则 1 2 3 4 class base { };class derived :public base{ };base b; derived d;
派生类的对象可以赋值给基类对象:b = d;
派生类的对象可以初始化基类引用:base & br = d;
派生类的对象的地址可以赋给基类指针:base * bp = & d;
注意上述规则不能颠倒,且如果派生方式是private
或者protected
,上述三条都不可行
7.直接基类与间接基类 C++中类的派生可以是很多层的 。
如类A派生类B,类B派生类C,类C派生类D:
类A是类B的直接基类;
类B是类C的直接基类,类A是类C的间接基类;
类C是类D的直接基类,类A、B是类D的间接基类;
在声明派生类时,只需要列出它的直接基类 ;派生类沿用着类的层次自动向上继承它的间接基类。
派生类的成员包括:派生类自己定义的成员,直接基类中的所有成员,所有间接基类的全部成员
当执行构造函数时,从顶层基类开始,依次往下执行基类的构造函数,最后执行自己的构造函数。
下面看一个例子:Base
$\to$ Derived
$\to$ MoreDerived
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 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(int i) :Derived(i) { cout << "MoreDerived constructed" << endl ; } ~MoreDerived() { cout << "MoreDerived destructed" << endl ; } }; int main () { MoreDerived Obj (3 ) ; return 0 ; }
程序运行结果为: