继承:
继承是面向对象三大特性之一
继承的基本语法: class 类名: public/protected/private 父类
总结:
继承的好处:可以减少重复的代码
class A : public B;
A 类称为子类 或 派生类
B 类称为父类 或 基类
派生类中的成员,包含两大部分:
1、一类是从基类继承过来的,一类是自己增加的成员。
2、从基类继承过过来的表现其共性,而新增的成员体现了其个性。
继承方式:
继承的语法:class 子类 : 继承方式 父类
继承方式一共有三种:
1、公共继承
2、保护继承
3、私有继承
相比于其他语言来说,c++还可以自己定义以哪种方式来继承父类,其他语言默认都是public来继承的
继承中的对象模型:
问题:从父类继承过来的成员,哪些属于子类对象中?
示例代码:
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};
//公共继承
class Son :public Base
{
public:
int m_D;
};
void test01()
{
cout << "sizeof Son = " << sizeof(Son) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
以上我们发现输出的字节为16个字节,但是还不够直观,我们可以通过visual studio相应的工具来查看
使用命令:cl /d1 reportSingleClassLayout查看的类名 所属文件名
结论: 父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到
继承中构造和析构顺序:
子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?
示例代码:
#include<iostream>
#include<string>
using namespace std;
class Father {
public:
Father() {
cout << "这是父类的构造方法" << endl;
}
~Father() {
cout << "这是父类的析构方法" << endl;
}
};
class Son :public Father {
public:
Son() {
cout << "这是子类的构造方法" << endl;
}
~Son() {
cout << "这是子类的析构方法" << endl;
}
};
void test01() { //因为我们需要看到析构方法的效果,所以我们在定义一个函数进行使用
Son s1;
}
int main() {
test01();
system("pause");
return 0;
}
结果:
总结:子类继承父类,先调用父类的构造方法然后再子类的,对于析构来说则顺序相反
继承同名成员处理方式:
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
1、访问子类同名成员 直接访问即可
2、访问父类同名成员 需要加作用域
示例代码:
#include<iostream>
#include<string>
using namespace std;
class Fat {
public:
void aaa() {
cout << "这是父类的aaa函数" << endl;
}
void bbb() {
cout << "这是父类的bbb函数" << endl;
}
};
class Son:public Fat {
public:
void aaa() {
cout << "这是子类的aaa函数" << endl;
}
};
int main() {
Son s1;
s1.aaa(); //这样只能调用子类的aaa函数
s1.Fat::aaa(); //同名的情况下 通过修改Fat:: 作用域来调用父类的同名函数
s1.bbb(); //不同名的情况下,可以直接调用父类的函数
system("pause");
return 0;
}
总结:
1、子类对象可以直接访问到子类中同名成员
2、子类对象加作用域可以访问到父类同名成员
3、当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
继承同名静态成员处理方式:
问题:继承中同名的静态成员在子类对象上如何进行访问?
#include<iostream>
#include<string>
using namespace std;
class Fat {
public:
static int a;
static void aaa() {
cout << "这是父类的aaa函数" << endl;
}
static void bbb() {
cout << "这是父类的静态bbb函数" << endl;
}
};
int Fat::a = 10;
class Son :public Fat {
public:
static int b;
static void bbb() {
cout << "这是子类的静态aaa函数" << endl;
}
};
int Son::b = 15;
int main() {
Son s1;
//通过对象来调用静态函数
s1.bbb(); // 调用子类的静态函数
s1.Fat::bbb(); // 同名的情况下,通过作用域来调用
s1.Fat::aaa(); // 没有同名,可以直接调用父类的静态函数
//通过类的方式来调用静态函数
Son::Fat::bbb(); // 第一个::是代表类名访问,第二个::代表访问父类的作用域下
system("pause");
return 0;
}
总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)
多继承语法:
C++允许一个类继承多个类 ps.在java中是不允许一个类继承多个类,但是一个类可以继承多个接口,然后这是课外话
语法: class 子类 :继承方式 父类1 , 继承方式 父类2...
C++实际开发中不建议用多继承,原因是多个继承,多个相同属性的话,都需要添加相应的作用域来进行调用,容易混淆
示例代码:
class Base1 {
public:
Base1()
{
m_A = 100;
}
public:
int m_A;
};
class Base2 {
public:
Base2()
{
m_A = 200; //开始是m_B 不会出问题,但是改为mA就会出现不明确
}
public:
int m_A;
};
//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
public:
int m_C;
int m_D;
};
//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
int main() {
test01();
system("pause");
return 0;
}
菱形继承: ps.之前学python的时候菱形继承好像跟这个不太一样emm,到时候再说吧,唉
菱形继承概念:
两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石继承
菱形继承问题:
羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
示例代码:
#include<iostream>
#include<string>
using namespace std;
class Animal {
public:
int age;
};
class Yang:virtual public Animal{};
class Tuo :virtual public Animal{};
class YangTuo:public Yang, public Tuo{};
int main() {
YangTuo ty1;
ty1.Yang::age = 38;
ty1.Tuo::age = 28;
cout << ty1.Yang::age << endl; //直接进行访问不明确,我们需要添加作用域,那我们就需要对父类的age进行赋值
cout << ty1.Tuo::age << endl;
cout << ty1.age << endl; //即使进行了上面两步赋值的操作还是不能直接访问当前对象的age,因为编译器还是不知道到底要访问哪一个,通过对virtual对父类进行修饰
system("pause");
return 0;
}
底层讲解原因参考:https://www.bilibili.com/video/av41559729?p=134
总结:
1、菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
2、利用虚继承可以解决菱形继承问题