一、继承的概念
1. 继承的定义
保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类
继承规则(私密程度:private > protected > public)
2. 继承的赋值兼容
切割、切片:
- 子类的值,地址,引用都可以赋值给父类
这里不是类型转换,而是语法支持,中间不会产生临时变量
- 但父类不能给子类,父类的指针和引用可以给子类,但注意可能存在越界问题,因为子类把父类看作一个子类的
3. 继承的作用域
-
在继承体系中基类和派生类都有独立的作用域
当遇到同名变量时遵从就近原则(全局和局部变量的话,优先访问局部,想要访问全局加上::)
在此假设上,如果子类和父类存在同名变量,当调用子类成员函数访问时,优先访问子类变量
这种现象叫隐藏/重定义
-
尽管继承了,如果想要访问父类成员必须指定作用域
4. 派生类的默认成员函数
- 基类成员调用基类成员的默认成员函数
- 子类的内置类型成员,调用自己的默认成员函数,自定义类型成员,调用自定义类型的成员函数
-
当父类没有默认成员函数,必须自己写
-
如果存在浅拷贝和资源释放的问题,系统的默认成员函数不能支持,必须自己写
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class parent
{
public:
parent(const char* name = "peter")
: _name(name)
{
cout << "parent()" << endl;
}
parent(const parent& p)
: _name(p._name)
{
cout << "parent(const parent& p)" << endl;
}
parent& operator=(const parent& p)
{
cout << "parent operator=(const parent& p)" << endl;
if (this != &p)
_name = p._name;
return *this;
}
~parent()
{
cout << "~parent()" << endl;
}
protected:
string _name;
};
class child :public parent
{
public:
//构造函数
child(const char* s = "hello", int num = 0)
:parent(s),
_num(num)
{}
//拷贝构造
child(const child& s)
:parent(s),
_num(s._num)
{}
//赋值重载
child& operator=(const child& s)
{
if (this != &s)
{
parent::operator=(s);
_num = s._num;
}
return *this;
}
void print()
{
cout << _name << endl;
cout << _num << endl;
}
private:
int _num;
};
int main()
{
child b = {"ball", 20};
child c;
c = b;
c.print();
return 0;
}
利用父类的构造函数完成子类的拷贝构造
原理:切片/切割(子类赋值给父类)
- 关于析构函数
析构函数名字会被统一处理成destructor()
那么子类的析构函数跟父类的析构函数就构成隐藏子类析构函数结束会自动调用父类析构函数
遵循先析构子类,再析构父类
5. 继承和友元
- 友元关系不能继承
6. 继承和静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类(继承多少次),都只有一 个static成员实例
7. 菱形继承和菱形虚拟继承
-
**单继承:**一个子类只有一个直接父类
-
**多继承:**一个子类有两个或以上直接父类
-
**菱形继承:**A类,分别先单继承到2个子类B和C,再多继承到一个D类
此时存在问题:
二义性,D类继承父类B和C中都有A类的成员,继承的是哪一个出现问题(当访问的时候不知道指的是谁的,需要指明作用域)
不使用virtual 但指明作用域
指明作用域后会存分别不同的两份,先继承的放在前面,后继承在后面
数据冗余,A类有个很大的数组,那D类要继承2份,会冗余
虚继承解决
在菱形的B、C类进行虚继承
class B : virtual public A
{};
class C : virtual public A
{};
此时再访问A成员不会出现二义性的报错(编译器优化成同一个)(在调试窗口会显示)
- 不使用virtual,指明作用域
内存地址会显示不同
同一个地址的数值发生了变化
- 当使用virtual,指明作用域时候
这时候会发现,此时,B、C共同继承的A在最后面
A为虚基类
D将其放在公共的地方,B、C可通过访问限定符去获取
如何获取?通过虚基表偏移量
前面存储一个指针可以指向虚基表,而虚基表存储着虚基类的偏移量(存储在哪)