环境: 编译器:CLion2021.3;操作系统:macOS Ventura 13.0.1
地表最强C++系列传送门:
「地表最强」C++核心编程(一)内存分区模型
「地表最强」C++核心编程(二)引用
「地表最强」C++核心编程(三)函数提高
「地表最强」C++核心编程(四)类和对象----封装
「地表最强」C++核心编程(五)文件操作——暂未更新
继承是C++的三大基本特性之一。
一、继承的基本语法
语法: class 子类 : 继承方式 父类
其中子类也称为派生类,父类也称为基类。派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。
class BasePage {
public:
void header() {
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer() {
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left() {
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
};
class Java : public BasePage {
public:
void content() {
cout << "JAVA学科视频" << endl;
}
};
class Python : public BasePage {
public:
void content() {
cout << "Python学科视频" << endl;
}
};
class CPP : public BasePage {
public:
void content() {
cout << "C++学科视频" << endl;
}
};
二、继承方式
继承方式一共有三种:公共继承、保护继承、私有继承。
2.1 public继承
class Base1 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base1 {
public:
void func() {
m_A = 10;
m_B = 10;
}
};
void myClass() {
Son1 s1;
s1.m_A;
}
2.2 protected继承
class Base2 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son2 : protected Base2 {
public:
void func() {
m_A;
m_B;
}
};
void myClass2() {
Son2 s;
}
2.3 private继承
class Base3 {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son3 : private Base3 {
public:
void func() {
m_A;
m_B;
}
};
class GrandSon3 : public Son3 {
public:
void func() {
}
};
2.4 继承规则
三、继承中的对象模型
父类中所有非静态成员属性都会被继承下去,私有成员属性只是访问不到,因为被编译器隐藏了,但是也被继承下去了。
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;
}
四、继承中的构造和析构顺序
子类继承父类后,当创建子类对象,也会调用父类的构造函数,此时先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。
class Base {
public:
Base() {
cout << "Base构造函数!" << endl;
}
~Base() {
cout << "Base析构函数!" << endl;
}
};
class Son : public Base {
public:
Son() {
cout << "Son构造函数!" << endl;
}
~Son() {
cout << "Son析构函数!" << endl;
}
};
void test01() {
Son s;
}
五、继承同名成员处理方式
当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
- 访问子类同名成员,直接访问即可,也就是
对象.属性 - 访问父类同名成员,需要加作用域,也就是
对象.父类::属性
需要注意的一点是:当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数,否则无法访问。强调一下,只要函数名相同,其他的不管一不一样,都会被隐藏。
class Base {
public:
int m_A;
public:
Base() {
m_A = 100;
}
void func() {
cout << "Base - func()调用" << endl;
}
void func(int a) {
cout << "Base - func(int a)调用" << endl;
}
};
class Son : public Base {
public:
int m_A;
public:
Son() {
m_A = 200;
}
void func() {
cout << "Son - func()调用" << endl;
}
};
void test01() {
Son s;
cout << "Son下的m_A = " << s.m_A << endl;
cout << "Base下的m_A = " << s.Base::m_A << endl;
s.func();
s.Base::func();
s.Base::func(10);
}
六、继承同名静态成员处理方式
静态成员和非静态成员出现同名,处理方式一致,只不过有两种访问的方式:通过对象和通过类名。比非静态多了一个通过类名访问,这是静态成员本身的特性所导致的。 关于静态成员,可以参考「地表最强」C++核心编程(四)类和对象—对象初始化和清理第八点。
class Base {
public:
static int m_A;
static void func() {
cout << "Base - static void func()" << endl;
}
static void func(int a) {
cout << "Base - static void func(int a)" << endl;
}
};
int Base::m_A = 100;
class Son : public Base {
public:
static int m_A;
static void func() {
cout << "Son - static void func()" << endl;
}
};
int Son::m_A = 200;
void test01() {
cout << "通过对象访问: " << endl;
Son s;
cout << "Son 下 m_A = " << s.m_A << endl;
cout << "Base 下 m_A = " << s.Base::m_A << endl;
cout << "通过类名访问: " << endl;
cout << "Son 下 m_A = " << Son::m_A << endl;
cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
void test02() {
cout << "通过对象访问: " << endl;
Son s;
s.func();
s.Base::func();
cout << "通过类名访问: " << endl;
Son::func();
Son::Base::func();
Son::Base::func(100);
}
七、多继承语法
C++允许一个类继承多个类,就是多继承。但多继承可能会引发从父类中继承多个同名成员,需要加作用域区分。C++实际开发中不建议用多继承。
语法: class 子类 : 继承方式 父类1 , 继承方式 父类2…
class Base1 {
public:
int m_A;
public:
Base1() {
m_A = 100;
}
};
class Base2 {
public:
int m_A;
public:
Base2() {
m_A = 200;
}
};
class Son : public Base2, public Base1 {
public:
int m_C;
int m_D;
public:
Son() {
m_C = 300;
m_D = 400;
}
};
void test01() {
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
八、菱形继承和虚继承
两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承,或者钻石继承。 菱形继承的问题: 1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。 2.草泥马继承自动物的数据继承了两份,但这个据我们只需要一份,这导致资源浪费而且毫无意义。 通过虚继承就可以解决菱形继承带来的问题。
语法: class 子类 : virtual 继承方式 父类
virtual关键字使得继承方式变成了虚继承,此时两个子类从同一父类那里继承来的是虚基类指针vbptr(virtual base pointer),这个指针指向了各自的vbtable(虚基类表),这个表中记录了一个偏移量,通过这个偏移量就可以找到需要的数据,这个数据只有一份。
class Animal {
public:
int m_Age;
};
class Sheep : virtual public Animal {
};
class Tuo : virtual public Animal {
};
class SheepTuo : public Sheep, public Tuo {
};
void test01() {
SheepTuo st;
st.Sheep::m_Age = 100;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
st.Tuo::m_Age = 200;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "st.m_Age = " << st.m_Age << endl;
}
|