一、继承的概念
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. 菱形继承和菱形虚拟继承
此时存在问题:
-
二义性,D类继承父类B和C中都有A类的成员,继承的是哪一个出现问题(当访问的时候不知道指的是谁的,需要指明作用域) 不使用virtual 但指明作用域 指明作用域后会存分别不同的两份,先继承的放在前面,后继承在后面 -
数据冗余,A类有个很大的数组,那D类要继承2份,会冗余
虚继承解决
在菱形的B、C类进行虚继承
class B : virtual public A
{};
class C : virtual public A
{};
此时再访问A成员不会出现二义性的报错(编译器优化成同一个)(在调试窗口会显示)
内存地址会显示不同
同一个地址的数值发生了变化
这时候会发现,此时,B、C共同继承的A在最后面
A为虚基类
D将其放在公共的地方,B、C可通过访问限定符去获取
如何获取?通过虚基表偏移量
前面存储一个指针可以指向虚基表,而虚基表存储着虚基类的偏移量(存储在哪)
|