继承的基本语法
继承:为了实现类似于网站公共头部和公共底部,用来避免代码冗余
继承的语法:class 子类 :继承方式 父类
别名:父类也称基类,子类也称派生类
#include <iostream>
using namespace std;
// 页面头部
class Head {
public:
// 构造函数
Head(){
cout << "这是页面头部" << endl;
}
};
// 页面尾部
class Tail {
public:
// 构造函数
Tail() {
cout << "这是页面尾部" << endl;
}
};
// Python 页面
class Python :public Head, public Tail{
public:
// 构造函数
Python() {
cout << "页面内容加载" << endl;
}
};
int main() {
// 实例化 Python 页面
Python p1;
// outputs:这是页面头部
// 这是页面尾部
// 页面内容加载
system("pause");
return 0;
}
从以上代码我们也可以分析出父类和子类的创建关系是:父类先创建、子类后创建
继承方式
继承的方式有三种:公共继承、保护继承和私有继承
第一步:先创建一个基类 A,并进行简单的赋值操作
class A {
public:
A() {
a = 1;
b = 2;
c = 3;
}
public:
int a;
protected:
int b;
private:
int c;
};
第二步:创建 B 类并使用公共继承的方式继承基类 A
// 公共属性和保护属性都被对应继承了下来,而私有属性不能被继承
class B :public A {
// 1. 从 A 类被继承下来的属性
//public:
// int a;
//protected:
// int b;
public:
// 1. 尝试访问 a 和 b
void func() {
cout << a + b << endl; // 3
}
// 2. 尝试访问 c
//void func() {
// cout << c << endl; // Error!
//}
};
第三步:创建 C 类并使用保护继承的方式继承基类 A
// 公共属性和保护属性都对应保护属性继承了下来,而私有属性不能被继承
class C :protected A {
// 从 A 类被继承下来的属性
//protected:
// int a;
// int b;
};
第四步:创建 D 类并使用私有继承的方式继承基类 A
// 公共属性和保护属性都对应私有属性继承了下来,而私有属性不能被继承
class D :private A {
// 1. 从 A 类被继承下来的属性
//private:
// int a;
// int b;
public:
// 2. 尝试在类内访问 a 和 b
void func() {
cout << a + b << endl; // 3
}
};
int main() {
D d1;
d1.func();
// 3. 尝试在类外访问私有成员变量
// d1.a = 1; // Error!
system("pause");
return 0;
}
继承中的对象模型
问题:从父类继承过来的成员有哪些是属于子类对象中的
重点:父类中所有非静态成员属性都被继承了下去;父类中私有成员属性是被编译器藏起来了,因此访问不到,但是确实被继承了下去
#include <iostream>
using namespace std;
class Base {
public:
int a;
protected:
int b;
private:
int c;
};
class Son :public Base{
public:
int d;
};
int main() {
Son s1;
cout << "sizeof of Son = " << sizeof(s1) << endl; // sizeof of Son = 16
system("pause");
return 0;
}
可以使用开发人员命令提示工具来具体查询类的模型
第一步:首先复制文件路径,打开开发人员命令提示工具后 cd 到此文件夹
第二步:键入如下命令
cl /d1 reportSingleClassLayout类名 文件名
第四步:显示具体模型
构造和析构顺序
子类继承父类后,当创建(释放)子类对象时,也会调用父类的构造(析构)函数
构造函数调用顺序:先调用父类的构造函数再调用子类的构造函数
析构函数调用顺序:先调用子类的析构函数再调用父类的析构函数
#include <iostream>
using namespace std;
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 func() {
Son s1;
}
int main() {
func();
// outputs:base 对象创建完成!
// son 对象创建完成!
// son 对象销毁完成!
// base 对象销毁完成!
system("pause");
return 0;
}
同名成员处理
问题:当子类和父类出现同名的成员时,如何通过子类对象访问到子类或父类中的成员
重点:可以直接访问子类同名成员;但是在访问父类同名成员时需要加上作用域(如果子类对象中出现和父类同名的成员函数,子类的同名函数会隐藏父类中所有同名成员函数)
#include <iostream>
using namespace std;
class Base {
public:
Base() {
m_a = 1;
}
void fun1() {
cout << "Base ~ fun1" << endl;
}
void fun1(int a) {
cout << "Base ~ fun1 + " << a << endl;
}
public:
int m_a;
};
class Son :public Base{
public:
Son() {
m_a = 2;
}
void fun1() {
cout << "Son ~ fun1" << endl;
}
public:
int m_a;
};
void func() {
Son s1;
// 属性
// 1. 直接输出子类同名成员
cout << s1.m_a << endl; // 2
// 2. 输出父类同名成员需要加作用域
cout << s1.Base::m_a << endl; // 1
// 方法
// 1. 直接输出子类同名成员
s1.fun1(); // Son ~ fun1
// 2. 输出父类同名成员需要加作用域
s1.Base::fun1(); // Base ~ fun1
s1.Base::fun1(100); // Base ~fun1 + 100
}
int main() {
func();
system("pause");
return 0;
}
同名静态成员处理
问题:继承中同名的静态成员在子类对象上如何访问
重点:可以直接访问子类同名静态成员;但是在访问父类同名静态成员时需要加上作用域(如果子类对象中出现和父类同名的成员函数,子类的同名函数会隐藏父类中所有同名成员函数)
#include <iostream>
using namespace std;
class Base {
public:
static void fun1() {
cout << "Base ~ fun1" << endl;
}
static void fun1(int a) {
cout << "Base ~ fun1 + " << a << endl;
}
public:
// 静态成员定义
static int m_a;
};
// 静态成员类外初始化
int Base::m_a = 1;
class Son :public Base{
public:
static void fun1() {
cout << "Son ~ fun1" << endl;
}
public:
static int m_a;
};
int Son::m_a = 2;
void func() {
Son s1;
// 1. 访问成员属性
cout << s1.m_a << endl; // 2
cout << s1.Base::m_a << endl; // 1
// 2. 访问成员方法
s1.fun1(); // Son ~fun1
s1.Base::fun1(); // Base ~fun1
s1.Base::fun1(2); // Base ~fun1 + 2
}
int main() {
func();
system("pause");
return 0;
}
多继承语法
C++ 允许一个类继承多个类
继承的语法:class 子类 :继承方式 1 父类 1,继承方式 2 父类 2…
重点:多继承可能会引发父类中同名成员的出现,需要加作用域进行区分
#include <iostream>
using namespace std;
class Base1 {
public:
Base1() {
m_a = 1;
}
int m_a;
};
class Base2 {
public:
Base2() {
m_a = 2;
}
int m_a;
};
class Son :public Base1, public Base2{
public:
Son() {
m_a = 3;
}
int m_a;
};
void func() {
Son s1;
// 此处仅用对象属性来演示
// 1. 访问子类对象属性
cout << s1.m_a << endl; // 3
// 2. 通过子类对象加上作用域访问父类对象属性
cout << s1.Base1::m_a << endl; // 1
// 3. 通过子类对象加上作用域访问父类对象属性
cout << s1.Base2::m_a << endl; // 2
}
int main() {
func();
system("pause");
return 0;
}
菱形继承
菱形继承概念:二个派生类继承自一个基类的同时又存在一个类同时继承自这两个派生类
#include <iostream>
using namespace std;
// 基类
class Animal {
public:
int Age_;
};
// 二个派生类
class Cattle :public Animal{
//public:
// int Age_;
};
class Horse :public Animal {
//public:
// int Age_;
};
// 基类
class CattleHorse :public Cattle, public Horse{
};
int main() {
// 1. 实例化牛马类
CattleHorse ch;
// 2. 通过牛马对象并加上作用域给父类对象 Cattle 属性赋值
ch.Cattle::Age_ = 20;
// 3. 通过牛马对象并加上作用域给父类对象 Horse 属性赋值
ch.Horse::Age_ = 10;
// 4. 于是出现问题牛马类的年龄究竟是使用 Cattle 的还是 Horse 的
cout << ch.Cattle::Age_ << endl; // 20
cout << ch.Horse::Age_ << endl; // 10
system("pause");
return 0;
}
利用虚继承可以完美的解决此问题
具体操作手段:继承之前加上关键字 virtual 变为虚继承,最初的基类称为虚基类
#include <iostream>
using namespace std;
// 基类
class Animal {
public:
int Age_;
};
// 二个派生类
class Cattle :virtual public Animal{
//public:
// int Age_;
};
class Horse :virtual public Animal {
//public:
// int Age_;
};
// 基类
class CattleHorse :public Cattle, public Horse{
};
int main() {
// 1. 实例化牛马类
CattleHorse ch;
// 2. 通过牛马对象并加上作用域给父类对象 Cattle 属性赋值
ch.Cattle::Age_ = 20;
// 3. 通过牛马对象并加上作用域给父类对象 Horse 属性赋值
ch.Horse::Age_ = 10;
// 4. 牛马类的年龄为最后一次赋值, ch.Horse::Age_ = 10;
cout << ch.Age_ << endl; // 10
ch.Age_ = 30;
cout << ch.Age_ << endl; // 30
system("pause");
return 0;
}