C++ Day8 类与对象 下 继承与多态
一、继承
- 减少重复的代码
1.1基础
- class 子类(派生类):继承方式 父类(基类);
1.2 继承方式
- 公共继承:class 子类(派生类):public 父类(基类);父类中公共的属性继承过来还是公共的,保护还是保护
- 保护继承:class 子类(派生类):protected父类(基类);
公共权限变成保护权限
,保护还是保护
- 私有继承:class 子类(派生类):private 父类(基类);公共和保护都变成私有权限
注意:
- 三种继承方式都
无法访问父类的私有属性
1.3 对象模型
- 父类的所有非静态成员属性都会被子类继承下去
父类的私有成员属性访问不到被编译器隐藏,但是继承了
。
#include<iostream>
using namespace std;
class person
{
public:
int a;
protected:
int b;
private:
int c;
};
class student:public person
{
public:
int d;
};
int main()
{
cout<<sizeof(student);//16
system("pause");
}
1.4 继承中构造和析构的顺序
- 现有父类构造函数再有子类构造函数和析构函数,最后父类析构函数
1.5 继承中同名成员
当子类和父类有相同名称的成员:
- 访问子类同名成员直接访问。
- 访问父类同名成员,加上作用域。
父类所有同名成员会被隐藏
#include<iostream>
using namespace std;
class person
{
public:
int a=11;
void fre()
{
cout<<a<<endl;
}
};
class student:public person
{
public:
int a=10;
void fre()
{
cout<<a<<endl;
}
};
int main()
{
student s;
cout<<s.a<<endl;
cout<<s.person::a<<endl;//加上作用域
s.fre();
s.person::fre();//加上作用域
system("pause");
}
1.6 同名静态成员
- 和上面处理方式一致
- 可以通过类名的方式访问:student::person::fre();
1.7 多继承
- 语法:class 子类:继承方式 父类1,继承方式 父类2;
- 不建议使用
1.8 菱形继承(钻石继承)
- 定义:两个子类继承同一个父类,又有子类同时继承上面两个类
- 问题:数据有两份造成,比如年龄有两个。
- 利用虚继承解决菱形继承问题。class student:virtual public person{};
- 虚继承底层:没有继承数据而是继承了虚基类指针(vbptr),该指正指向一个虚基类表(vbtable),该表中纪录偏移量,通过偏移量找到数据。
二、多态
2.1 基础
分类:
- 静态多态:包括函数重载和运算符重载,复用函数名。
- 动态多态:子类和虚函数实现运行时多态。
区别:
- 静态多态:函数地址
早绑定
:编译阶段确定地址 - 动态多态:函数地址
晚绑定
:运行阶段确定地址
虚函数实现地址的晚绑定
#include<iostream>
using namespace std;
class person
{
public:
virtual void say()//加上virtual后变成虚函数,该函数的地址在运行阶段绑定
{
cout<<"人说话"<<endl;
}
};
class student:public person
{
public:
void say()
{
cout<<"学生说话"<<endl;
}
};
void test(person &person)//地址早绑定
{
person.say();
}
int main()
{
student s;
test(s);//想让学生说话,结果是人,因为早绑定,地址在编译阶段已经确定
system("pause");
}
动态多态的条件:
- 1.有继承关系
- 2.子类重写父类的虚函数,重写:函数的返回值,函数名,参数列表完全相同
- 动态多态底层原理:虚函数生成虚函数指针(vfptr)指向一个虚函数表,虚函数表中存储函数的地址,当子类重写父类虚函数时,子类将子类的虚函数表中地址指向自己,当父类引用指向子类对象时,会去找子类的虚函数表中地址。
2.2 纯虚函数和抽象类
- 通常父类虚函数的实现无意义,可将虚函数改为纯虚函数
- 语法:virtual 返回值类型 函数名(参数列表)=0;
- 抽象类:有纯虚函数的类
- 抽象类无法实例化对象,子类必须重写抽象类中的纯虚函数
2.3 多态示例
做饮品:
#include<iostream>
using namespace std;
class Abstractdrink//抽象类
{
public:
virtual void water()=0;
virtual void wash()=0;
virtual void pull()=0;
virtual void addmaterial()=0;
virtual void make()
{
water();
wash();
pull();
addmaterial();
}
};
class coffee: public Abstractdrink
{
void water()
{
cout<<"1.煮水"<<endl;
}
void wash()
{
cout<<"2.冲泡咖啡"<<endl;
}
void pull()
{
cout<<"3.倒入杯中"<<endl;
}
void addmaterial()
{
cout<<"4.加糖"<<endl;
}
public:
};
class tea: public Abstractdrink
{
void water()
{
cout<<"1.煮水"<<endl;
}
void wash()
{
cout<<"2.冲泡茶叶"<<endl;
}
void pull()
{
cout<<"3.倒入杯中"<<endl;
}
void addmaterial()
{
cout<<"4.柠檬"<<endl;
}
public:
};
void makedrink(Abstractdrink &drink)
{
drink.make();//一个接口实现多个
}
int main()
{
tea t;
coffee c;
makedrink(t);//一个接口实现多个
cout<<endl;
makedrink(c);//一个接口实现多个
system("pause");
}
2.4 虚析构和纯虚析构
- 使用多态是父类指针无法释放子类的析构代码,导致内存泄露。
- 将父类中的析构函数改为虚析构或纯虚析构。
- 纯虚析构既要声明又要实现