教我们这些凡人贪恋一生的,不正是眼前这些渺小的幸福嘛
封装、多态、继承是面向对象的三大特性。
继承
继承可以减少重复代码,继承后子类中成员包含两部分:自己增加的成员和继承的成员。
语法 : 基类(父类):继承方式 派生类(子类)
继承方式 有三种公有继承、保护继承、私有继承。
#include <iostream>
using namespace std;
class Base
{
public:
int m_size1;
protected:
int m_size2;
private:
int m_size3;
};
class Son1:public Base//公共继承
{
public:
void fuc()
{
m_size1 = 10;//公共继承后,在Son1子类中,m_size1为公共权限
m_size2 = 20;//公共继承后,在Son1子类中,m_size2为保护权限
//m_size3 = 0;//m_size3为Base类中的私有私有成员,子类不可访问
}
};
class Son2 :protected Base//保护继承
{
public:
void fuc()
{
m_size1 = 10;//保护继承后,在Son2子类中,m_size1为保护权限
m_size2 = 20;//保护继承后,在Son2子类中,m_size2为保护权限
//m_size3 = 0;//m_size3为Base类中的私有私有成员,子类不可访问
}
};
class Son3 :private Base//私有继承
{
public:
void fuc()
{
m_size1 = 10;//私有继承后,在Son3子类中,m_size1为私有权限
m_size2 = 20;//私有继承后,在Son3子类中,m_size2为私有权限
//m_size3 = 0;//m_size3为Base类中的私有私有成员,子类不可访问
}
};
class GrandSon :public Son3
{
public:
void fuc()
{
//m_size1 = 10;//父类Son3的私有成员,子类不可访问
}
};
void test1()
{
Son1 son1;
son1.m_size1;
//son2.m_size2;//保护权限类外不可访问
}
void test2()
{
Son2 son2;
//son2.m_size1;//保护权限类外不可访问
//son2.m_size2;//保护权限类外不可访问
}
int main()
{
test1();
test2();
system("pause");
return 0;
}
继承中的对象模型
父类中所有的非静态成员属性都会被子类继承,编译器会隐藏父类中的私有成员属性,因此子类无法访问,实际上子类仍然继承了这一部分。
#include <iostream>
using namespace std;
class Base
{
public:
int m_size1;
protected:
int m_size2;
private:
int m_size3;
};
class Son :public Base
{
public:
int m_size4;
};
void test()
{
cout << "Son类在内存中占"<<sizeof(Son)<<"字节" << endl;//继承12字节加上自己特有的4字节,共计16字节
}
int main()
{
test();
system("pause");
return 0;
}
打开开发人员命令提示工具查看对象的内存占用情况:
1.进入文件所在的目录 cd 绝对路径(如果不在C盘下,先跳转盘符 F:(跳转F盘))
2.然后输入 cl /d1 reportSingleClassLayout查看的类名 所属文件名
继承中构造和析构顺序
继承中的构造和析构顺序:先构造父类,再构造子类,先析构子类,再析构父类
继承中同名的成员处理
当子类跟父类拥有同名的成员函数或者属性时,子类会隐藏父类中所有版本的同名函数(包括返回值和参数不同的同名函数)。
子类中的成员属性和函数直接访问即可。
对于一般的成员属性和成员函数,通过添加作用域的方式来访问父类的同名成员属性或者函数。
对于静态的成员属性和成员函数,除了通过作用域的方式之外,还可以通过类名的方式访问。
#include <iostream>
using namespace std;
class Base
{
public:
int m_size1=10;
static int m_size2;//静态成员属性类内声明
void fuc_1()
{
cout << "Base类的fuc_1函数" << endl;
}
void fuc_1(int a)
{
cout << "Base类的传参fuc_1函数" << endl;
}
static void fuc_2()
{
cout << "Base类的静态fuc_2函数" << endl;;
};
};
int Base::m_size2 = 20;//静态成员属性类外定义
class Son :public Base
{
public:
int m_size1=30;
static int m_size2; //静态成员属性类内声明
void fuc_1()
{
cout << "son类的fuc_1函数" << endl;
}
static void fuc_2()
{
cout << "son类的静态fuc_2函数" << endl;;
};
};
int Son::m_size2 = 40; //静态成员属性类内定义
void test()
{
Son son;
cout << "son中m_size1为" << son.m_size1 << endl;//直接访问子类自身的同名属性
cout << "son的继承的父类m_size1为" << son.Base::m_size1 << endl;//
cout << "son的继承的父类静态父类m_size2为" << Son::Base::m_size2 << endl;//通过类名访问父类中静态同名属性
cout << "son中静态成员属性m_size2为" << son.m_size2 << endl;//直接访问子类自身的静态同名属性
cout << "son中继承的父类静态成员属性m_size2为" << son.Base::m_size2 << endl;//通过添加作用域访问父类的静态同名成员属性
son.fuc_1();//直接访问子类自身的同名函数
son.fuc_2();//直接访问子类自身的静态同名函数
son.Base::fuc_1(); //通过添加作用域访问父类的同名成员函数
son.Base::fuc_1(10);//通过添加作用域访问父类的有参同名成员函数
son.Base::fuc_2();//通过添加作用域访问父类的静态同名成员函数
Son::Base::fuc_2();//通过类名访问父类中静态同名函数
}
int main()
{
test();
system("pause");
return 0;
}
多继承语法
多继承语法: class 子类:继承类型 父类1,继承类型 父类2.
实际开发中不建议使用。
#include <iostream>
using namespace std;
class Base1
{
public:
int m1=10;
int m2=20;
};
class Base2
{
public:
int m1=11;
int m3=30;
};
class Son :public Base1, public Base2//Son多继承的两个父类Base1、Base2
{
public:
int m4=40;
};
void test()
{
Son son;
//添加作用域以区分多继承中父类的同名属性
cout << "打印父类Base1中的m1的值" << son.Base1::m1 << endl;
cout << "打印父类Base2中的m2的值" << son.Base2::m1 << endl;
}
int main()
{
test();
system("pause");
return 0;
}
菱形继承
两个子类Base1、Base2继承了同一个父类Base0,又有一个类Son同时继承了这两个子类Base1、Base2,这种继承关系叫做菱形继承(钻石继承)。
菱形继承会出现一个问题,最底层的子类Son中会有两份最上层父类Base0的数据,通过虚继承的方式解决该问题。
#include <iostream>
using namespace std;
class Base0
{
public:
int m1=1;
};
class Base1:virtual public Base0//继承前加virtual关键字后,变为虚继承,此时公共的父类Animal称为虚基类
{
public:
int m2=2;
};
class Base2 :virtual public Base0//继承前加virtual关键字后,变为虚继承,此时公共的父类Animal称为虚基类
{
public:
int m3=3;
};
class Son :public Base1, public Base2//Son多继承的两个父类Base1、Base2
{
public:
int m4=4;
};
void test()
{
Son son;
cout << "打印Son中的m1的值" << son.m1 << endl;
}
int main()
{
test();
system("pause");
return 0;
}
使用虚继承前后son对象占用内存对比如下:
其中,vbptr是虚基指针,vbtable是虚基类表,虚基指针指向虚基类表。