继承
格式
继承方式
(1)子类拥有父类所有的成员变量和函数.
(2)子类可以拥有父类没有的方法和属性。
class Student
{
public:
Student(string s,int g,int a)
{
cout <<"Student" << endl;
name=s.grade=g,age=a;
}
void printf()
{
cout << "Student" << endl;
cout << "name=" << name << endl;
cout << "grade=" <<grade << endl;
cout << "age" << age << endl;
}
protected:
string name;
int grade;
private:
int age;
};
公有继承
class GraduateStudent : public Student
{
public:
Graduate(string s,int g,int a):Student(s,g,a)
{
cout << "Graduate Student " << endl;
}
void printf()
{
cout << "Graduate Student" << endl;
cout << "name="<< name << "grade="<< grade << endl;
}
};
void main()
{
Graduate g("Ming",95,21);
g.print();
g.print();
}
打印结果:
Student
Graduate
Student name=Ming ,grade=95,age=21
Graduate name=Ming,grade=95,age=21
1.父类私有成员,子类不能访问
2.父类保护成员,子类可以继承为自己的保护成员,在子类可以访问,在外部不能访问。
3.父类公有成员,子类可以继承为自己的公有成员,内外都可以访问。
私有继承
- 父类私有成员,子类不能访问
- 父类保护成员,子类可以继承为自己的保护成员,在派生类可以访问,在外部不能访问。
- 父类公有成员,子类可以继承为自己的保护员,在派生类可以访问,在外部不能访问。
保护继承
- 父类私有成员,子类不能访问
- 父类保护成员,子类可以继承为自己的私有成员,在派生类可以访问,在外部不能访问。
- 父类公有成员,子类可以继承为自己的私有成员,在派生类可以访问,在外部不能访问。
总结
- 基类私有成员无论以什么方式继承在派生类中都是不可见的,不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
- 基类私有成员是在派生类中不能被访问,若想在派生类中访问基类的私有成员,则定义为protected,可以看出保护成员限定符是因继承才出现的
- 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
基类和派生类对象赋值转换
- 派生类对象可以赋值给基类的对象/指针/引用
- 基类对象不能给派生类对象赋值
- 基类指针可以通过强制类型转换赋值给派生类指针
继承中的作用域
- 继承体系中基类和派生类都有独立的作用域
- 派生类和基类当中有同名成员,派生类将屏蔽基类对同名成员的访问,这种情况叫隐藏,也叫重定义。
- 如果是成员函数的隐藏,只要函数名相同就能构成隐藏
- 同名函数优先访问派生类
派生类的默认成员函数
构造函数
- 派生类构造函数必须调用基类构造函数,如果基类没有默认的构造函数,必须在派生类构造函数初始化阶段显示调用。
- 如果基类存在缺省的构造函数,派生类的构造函数可以实现也可以不实现。
总结:
无论如何,派生类构造函数都要在初始化列表处调用基类的构造函数。
子类对象在创建时会首先调用父类构造函数
父类狗仔函数执行结束后,执行子类的构造函数
拷贝构造函数
派生类调用基类的拷贝构造函数。
析构函数
执行完所有函数后自动调用基类的析构函数。
先析构派生类,再析构成员变量,最后析构基类。
先构造的后析构。
基类构造函数 |
---|
派生类构造函数 |
派生类行为 |
~派生类析构函数 |
~基类析构函数 |
禁止继承
在类名后加final关键字,表示禁止继承
class NonInherit final
{};
继承与静态成员
基类定义的static成员,则整个继承体系中只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例。
菱形继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承是多继承的一种特殊情况。
class Person
{
public:
int name;
};
class Student:public Person
{
public:
int _num;
};
class Teacher :public Person
{
public:
int id;
};
class A:public class Student,class Teacher
{
public:
int _age;
};
int main()
{
A.a;
a.name="Ming";//这样无法明确知道是访问哪一个类
a::Student::name="Ming";
a::Teacher::name="Ming";
}
虚拟菱形继承
菱形继承会造成数据冗余:
- 虚拟继承中,给每个对象多分配了4个字节,在构造函数中构建,给对象的前4个字节赋值。
- 对象模型:基类部分在下,派生类在上
- 编译器给虚拟继承的派生类生成了一个默认的构造函数,并且传递实参1。作用:检测继承是否为虚拟继承。
编译器给虚拟继承的派生类多分配的这4个字节用来存放地址,这个地址指向一个内存块,内存块存放派生类对象的偏移量,构成一张虚基表。
派生类访问自己的成员:直接访问
派生类访问基类成员:通过虚基表访问
B:int b;
C1:virtual public B:int c1;
C2:virtual public B:int c2;
D:public C1,public C2:int d;
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。