1.继承和派生的概念
一个新类从已有的类中获得其属性和方法,这种现象称为类的 继承
。
从已有的类产生新的子类的现象,称为类的 派生
。
被继承的类称为 基类
,派生的类称为 派生类或子类
。
派生类是抽象基类的实例。
2.派生类的声明
公用继承
class student1:public student
私有继承
class student1:private student
保护继承
class student1:protected student
注意
:如果不写继承方式,则默认为私有(private)继承。
3.派生类的构成
派生类的构成包括:
(1)继承基类的所有成员
,包括数据成员和成员函数,获得共性。全部继承(除构造函数和析构函数),没有选择,所以可能会造成基类方法在派生类中用不到,而导致大量冗余。
(2)自增加的成员
,包括数据成员和成员函数。体现个性。
4.派生类成员的访问属性
(1)公用继承
继承方式指定为 public
。
在基类的访问属性 | 继承方式 | 在派生类的访问属性 |
---|---|---|
public | public | public |
private | public | 不可访问 |
protected | public | protected |
注意
:在公用派生类时,只能通过基类的公用成员函数来引用基类的私有数据成员
。
(2)私有继承
继承方式指定为 private
。
在基类的访问属性 | 继承方式 | 在派生类的访问属性 |
---|---|---|
public | private | private |
private | private | 不可访问 |
protected | private | protected |
注意
:既然声明为私有继承,就表示将原来能被外界引用的成员隐藏起来,不让外界引用。
。
(3)保护继承
继承方式指定为 protected
。
如果基类声明了私有成员,那么任何派生类都是不能访问它们的的;若希望派生类可以访问它们,应当把它们声明为保护成员。
在基类的访问属性 | 继承方式 | 在派生类的访问属性 |
---|---|---|
public | protected | protected |
private | protected | 不可访问 |
protected | protected | protected |
注意
:基类中的保护成员只有派生类可以访问,友元可以访问基类的私有成员
。
保护成员和私有成员的区别在于把保护成员的范围扩展到派生类中
。
(4)多级派生访问属性
多级派生遵循单次派生的规则,依次叠加。
在实际中,常用多级公用派生
。因为如果是私有派生或保护派生的话,多次派生之后,容易分不清楚哪些是私有、保护成员。
5.派生类的构造函数和析构函数
(1)派生类的构造函数
基类的构造函数是不能被继承的,故 在执行派生类的构造函数时,调用基类的构造函数
,这样基类和派生类同时被初始化。
<1>在派生类 类体内 定义派生类构造函数:
Student1(int n, string nam, char s, string con, string f_nam, int ag, string ad):Student(n, nam, s, con, f_nam)
{
age = ag;
addr = ad;
}
<2>在派生类 类体外 定义派生类构造函数:
//派生类类体中 声明:
Student1(int n, string nam, char s, string con, string f_nam, int ag, string ad);
//派生类类体外 定义:
Student1::Student1(int n, string nam, char s, string con, string f_nam, int ag, string ad):Student(n, nam, s, con, f_nam)
{
age = ag;
addr = ad;
}
注意
:在类中对派生类构造函数做声明时,不能包括基类的构造函数部分,只在定义时将它列出。
执行派生类构造函数顺序:基类->派生类
。
(2)有子对象的派生类的构造函数
在派生类Student1中,存在数据成员 Student monitor。此时,派生类构造函数需要加上子对象的初始化
,基类和子对象的初始化顺序任意。编译系统依据相同参数名来确定它们的传递关系的。
class Student1: public Student
{
public:
Student1(int n, string nam, char s, string con, string f_nam, int n1, string nam1, char s1, string con1, string f_nam1, int ag, string ad):Student(n, nam, s, con, f_nam), monitor(n1, nam1, s1, con1, f_nam1)
{
age = ag;
addr = ad;
}
void show();
void show_monitor();
~Student1();
private:
Student monitor;
string addr;
int age;
};
执行派生类构造函数顺序:基类->子对象->派生类
。
(3)多层派生的构造函数
class Student1: public Student
{
Student1(...):Student(...)
{ ... }
}
class Student2: public Student1
{
Student2(...):Student1(...)
{ ... }
}
注意
:不要列出每一层派生类的构造函数,只需写出其上一层(直接基类)的构造函数
。
执行派生类构造函数顺序:基类->派生类1->派生类2
。
(4)派生类构造函数特殊形式
<1>当不需要新增派生类的数据成员时,派生类的构造函数体可以为空;
<2>当基类的构造函数没有参数或没有定义时,定义派生类的构造函数时可以不写基类的构造函数。如果基类的构造函数有参数,则必须写。
(5)派生类的析构函数
调用顺序与调用构造函数相反。派生类->基类
。
6.多重继承
class D: public A, private B, protected C
{}
多重继承时,重名成员会导致二义性,因此对同名成员加上作用域("::")。多重继承也会导致数据冗余。
注意:
<1>派生类新增的同名成员会 覆盖
基类中的同名成员;
<2>不同的成员函数,只有在函数名,参数个数和类型都相同时,才会发生 同名覆盖
。如果只有函数名相同,则属于 函数重载
。
7.虚基类
(1)虚基类构建
如果一个派生类有多个直接基类,而这些基类又有一个共同的基类,则该派生类中会保留该间接共同基类的多份同名成员。
虚基类:在继承间接共同基类时只保留一份成员。
class A
{...};
class B:virtual public A
{...};
class C:virtual public A
{...};
注意
:
<1>虚基类在声明派生类、指定继承方式时声明的;
<2>派生类只继承间接基类一次,基类成员只保留一次;
<3>应当在基类的所有直接派生类中声明为虚基类,以保证虚基类在派生类中只继承一次。
(2)虚基类初始化
class A
{A(int n){}
...};
class B:virtual public A
{B(int n):A(n){}
...};
class C:virtual public A
{C(int n):A(n){}
...};
class D:public B, public C
{D(int n):A(n), B(n), C(n){}
...};
注意
:最后的派生类不仅要对直接基类进行初始化,还要对虚基类进行初始化
。
C++编译系统只执行最后的派生类对虚基类的构造函数的调用,忽略其他派生类对虚基类构造函数的调用。
8.基类与派生类的转换
(1)派生类对象可以对基类对象赋值;
(2)派生类对象可以代替基类对象向基类对象的引用进行赋值或初始化;
(3)如果函数参数是基类对象或基类对象的引用,相应的实参可以用子类对象。
(4)指向基类对象的指针变量也可以指向派生类对象。通过指向基类对象的指针只能访问派生类中的基类成员,而不能访问派生类的增加成员。