author:&Carlton
tag:C++
topic:【C++】继承基础知识及简单应用,使用reportSingleClassLayout(在Visual Studio开发人员命令提示窗口)查看派生类详细信息
website:黑马程序员C++
date:2023年7月22日
目录
概要
继承是C++面向对象程序设计的三大特性之一(封装,继承,多态),既能有效利用已定义好的类,又允许添加之前定义的类中没有的代码,有效缩减代码量,使得程序更加简洁。
继承规则
class 子类名 : 继承方式 父类名 { };
继承方式有public,protected,private
对象模型
父类所有成员都继承,只是根据继承方式不同继承得到的成员访问权限也相应改变:
(父类的私有成员子类继承,但均无法访问,需要声明友元,以下说明不包括父类私有成员)
①公开继承public: 访问权限相应继承
②保护继承protected: 全定为protected
③私有继承private: 全部变为private
注:成员包括属性和函数
子类对象模型结构(具体操作步骤在本文最后一小节说明):
说明子类继承父类所有成员的例子:
cpp
#include <iostream>
using namespace std;
class Base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Son:public Base
{
public:
int m_d;
};
void test01()
{
Son s1;
cout << sizeof(s1) << endl;
}
int main()
{
test01();
return 0;
}
构造析构顺序
先构造父类,然后构造子类,析构顺序与构造顺序相反。
源代码:
cpp
#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 test01()
{
Son s1;
}
int main()
{
test01();
return 0;
}
程序运行结果:
同名与静态同名成员
需要添加作用域,对于静态成员还能通过类访问。
Son::Base::m_b << endl;
与Base::m_b含义不同,这里是在子类Son中访问父类Base作用域下的m_b静态成员变量
cpp
#include <iostream>
using namespace std;
class Base
{
public:
Base():m_a(100)//父类的初始化为100
{
}
int m_a;
void func()
{
cout << "Base-func()" << endl;
}
void func(int a)
{
cout << "Base-func(a)" << endl;
}
//子类有同名的成员函数时父类的成员函数被隐藏,无法直接使用占位参数区分,还是需要添加作用域区分
static int m_b;
};
//静态成员变量类内声明,类外定义初始化
int Base::m_b = 100;
class Son:public Base
{
public:
Son():m_a(200)//子类的初始化为200
{
}
void func()
{
cout << "Son-func()" << endl;
}
int m_a;//有同名成员
static int m_b; //有静态同名成员
};
int Son::m_b = 200;
void test01()
{
Son s1;
cout << "子类m_a:" << s1.m_a << endl;
cout << "父类m_a:" << s1.Base::m_a << endl; //添加作用域
s1.func();
s1.Base::func(); //添加作用域
s1.Base::func(10);
//通过对象访问静态成员
cout << "子类m_b:" << s1.m_b << endl;
cout << "父类m_b:" << s1.Base::m_b << endl;
//通过类访问静态成员
cout << "子类m_b:" << Son::m_b << endl;
cout << "父类m_b:" << Son::Base::m_b << endl; //与Base::m_b含义不同,这里是在子类Son中访问父类Base作用域下的m_b静态成员变量
}
int main()
{
test01();
return 0;
}
多继承
class 子类名 : 继承方式 父类名1,继承方式 父类名2,......{ } ;
多继承可能会带来同名成员 的问题,但在实际开发过程中这块代码可能由几个人一起做,如果大量使用多继承则出现同名成员问题时寻找作用域会很麻烦。
源代码
cpp
#include <iostream>
using namespace std;
class Base1
{
public:
int m_a;
};
class Base2
{
public:
int m_b;
};
class Son :public Base1, public Base2 //继承Base1和Base2
{
int m_c;
int m_d;
};
void test01()
{
cout << "sizeof(Son):" << sizeof(Son) << endl;
}
int main()
{
test01();
return 0;
}
多继承对象模型结构(具体操作步骤见本文最后一节):
菱形继承
菱形继承:A被B、C继承,D继承B、C
解决菱形继承的两个问题:①二义性矛盾 ②资源浪费
①二义性矛盾:"孙子不知道从父1那访问爷爷的东西还是从父2那里访问"
②资源浪费:无论从父1还是从父2,得到的东西是一样的,不需要得到两次
解决方法:
①添加作用域
②使用关键字virtual虚继承
源代码:
cpp
#include <iostream>
#include <string>
using namespace std;
/*
* 解决菱形继承的两个问题:①二义性矛盾 ②资源浪费
* ①二义性矛盾:"孙子不知道从父1那访问爷爷的东西还是从父2那里访问"
* ②无论从父1还是从父2,得到的东西是一样的,不需要得到两次
*/
class School
{
public:
School():m_slogan("厚德载物,格物致知")
{
}
string m_slogan;
};
class Teacher1 :virtual public School {}; //加关键字virtual,虚继承
class Teacher2 :virtual public School {};
class Student :public Teacher1, public Teacher2 {};
void test01()
{
Student s1;
//cout << s1.m_slogan << endl; //有二义性错误,m_slogan是Teacher1的还是Teacher2的
cout << s1.Teacher1::m_slogan << endl; //通过作用域解决第一个问题:二义性错误
cout << s1.Teacher2::m_slogan << endl;
cout << s1.m_slogan << endl; //使用virtual虚继承可以不加作用域,都通过虚拟指针指向基类School的m_slogan,是同一个成员变量
}
int main()
{
test01();
return 0;
}
对象模型结构(具体操作步骤见本文最后一节):
使用虚继承前:
使用虚继承后:
vbtable:虚基类表
vb:virtual base 虚基类
ptr:pointer 指针
数字代表位移量
VS开发人员命令操作提示窗口
系统菜单里找到:
跳转盘符:
锁定文件路径(包含源文件的上一级目录):
应用:
①查看该文件路径下文件情况
②查看类对象模型结构
欢迎指正与分享,谢谢!