目录
1.继承的基本概念
概括:从继承开始就是对代码进行复用的学习,继承可以理解为一个类从另一个类获取成员变量和成员函数的过程。目的是重复使用代码。
class 派生类名:[继承方式]基类名
{
派生类新增加的成员
};
被继承的类称为基类或父类,继承的类称为派生类或子类。
继承和派生是一个概念,只是站的角度不同。
派生类除了拥有基类的成员,还可以定义新的成员,以增强其功能。
使用继承的场景:
1) 如果新创建的类与现有的类相似,只是多出若干成员变量或成员函数时,可以使用继承。
2) 当需要创建多个类时,如果它们拥有很多相似的成员变量或成员函数,可以将这些类共同的成员提取出来,定义为基类,然后从基类继承。
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CAllComers // 海选报名者类
{
public:
string m_name; // 姓名
string m_tel; // 联系电话
// 构造函数。
CAllComers() { m_name = "某女"; m_tel = "不详"; }
// 报名时需要唱一首歌。
void sing() { cout << "我是一只小小鸟。\n"; }
// 设置姓名。
void setname(const string& name) { m_name = name; }
// 设置电话号码。
void settel(const string& tel) { m_tel = tel; }
};
class CGirl :public CAllComers // 超女类
{
public:
int m_bh; // 编号。
CGirl() { m_bh = 8; }
void show() { cout << "编号:" << m_bh << ",姓名:" << m_name << ",联系电话:" << m_tel << endl; }
};
int main()
{
CGirl g;
g.setname("西施");
g.show();
}
2、继承方式
类成员的访问权限由高到低依次为:public --> protected --> private,public成员在类外可以访问,private成员只能在类的成员函数中访问。
如果不考虑继承关系,protected成员和private成员一样,类外不能访问。但是,当存在继承关系时,protected和private就不一样了。基类中的protected成员可以在派生类中访问,而基类中的 private成员不能在派生类中访问。
继承方式有三种
public(公有的)、protected(受保护的)和private(私有的)。它是可选的,如果不写,那么默认为private。不同的继承方式决定了在派生类中成员函数中访问基类成员的权限。
1)基类成员在派生类中的访问权限不得高于继承方式中指定的权限。例如,当继承方式为protected时,那么基类成员在派生类中的访问权限最高也为protected,高于protected的会降级为protected,但低于protected不会升级。再如,当继承方式为public时,那么基类成员在派生类中的访问权限将保持不变。
也就是说,继承方式中的public、protected、private是用来指明基类成员在派生类中的最高访问权限的。
2) 不管继承方式如何,基类中的private成员在派生类中始终不能使用(不能在派生类的成员函数中访问或调用)。
3) 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为public 或protected;只有那些不希望在派生类中使用的成员才声明为private。
4) 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected。
由于private和protected继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以,在实际开发中,一般使用public。
在派生类中,可以通过基类的公有成员函数间接访问基类的私有成员。
使用 using 关键字可以改变基类成员在派生类中的访问权限。
注意:using只能改变基类中public和protected成员的访问权限,不能改变private成员的访问权限,因为基类中的private成员在派生类中是不可见的,根本不能使用。
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class A { // 基类
public:
int m_a=10;
protected:
int m_b=20;
private:
int m_c = 30;
};
class B :public A // 派生类
{
public:
using A::m_b; // 把m_b的权限修改为公有的。
private:
using A::m_a; // 把m_a的权限修改为私有的。
};
int main()
{
B b;
// b.m_a = 11;
b.m_b = 21;
//b.m_c = 21;
}
3、继承的对象模型
1)创建派生类对象时,先调用基类的构造函数,再调用派生类的构造函数。
2)销毁派生类对象时,先调用派生类的析构函数,再调用基类的析构函数。如果手工调用派生类的析构函数,也会调用基类的析构函数。
3)创建派生类对象时只会申请一次内存,派生类对象包含了基类对象的内存空间,this指针相同的。
4)创建派生类对象时,先初始化基类对象,再初始化派生类对象。
5)在VS中,用cl.exe可以查看类的内存模型。
6)对派生类对象用sizeof得到的是基类所有成员(包括私有成员)+派生类对象所有成员的大小。
7)在C++中,不同继承方式的访问权限只是语法上的处理。
8)对派生类对象用memset()会清空基类私有成员。
9)用指针可以访问到基类中的私有成员(内存对齐)。
查看对象内存布局的方法:
cl 源文件名 /d1 reportSingleClassLayout类名
注意:类名不要太短,否则屏幕会显示一大堆东西,找起来很麻烦。
例如,查看BBB类,源代码文件是demo01.cpp:
cl demo01.cpp /d1 reportSingleClassLayoutBBB
cl命令环境变量:
1)在PATH环境变量中增加cl.exe的目录
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\Hostx64\x64
2)增加INCLUDE环境变量,内容如下:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um
C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\winrt
3)增加LIB环境变量,内容如下:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\lib\x64
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x64
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
void* operator new(size_t size) // 重载new运算符。
{
void* ptr = malloc(size); // 申请内存。
cout << "申请到的内存的地址是:" << ptr << ",大小是:" << size << endl;
return ptr;
}
void operator delete(void* ptr) // 重载delete运算符。
{
if (ptr == 0) return; // 对空指针delete是安全的。
free(ptr); // 释放内存。
cout << "释放了内存。\n";
}
class A { // 基类
public:
int m_a = 10;
protected:
int m_b = 20;
private:
int m_c = 30;
public:
A() {
cout << "A中this指针是: " << this << endl;
cout << "A中m_a的地址是:" << &m_a << endl;
cout << "A中m_b的地址是:" << &m_b << endl;
cout << "A中m_c的地址是:" << &m_c << endl;
}
void func() { cout << "m_a=" << m_a << ",m_b=" << m_b << ",m_c=" << m_c << endl; }
};
class B :public A // 派生类
{
public:
int m_d = 40;
B() {
cout << "B中this指针是: " << this << endl;
cout << "B中m_a的地址是:" << &m_a << endl;
cout << "B中m_b的地址是:" << &m_b << endl;
//cout << "B中m_c的地址是:" << &m_c << endl;
cout << "B中m_d的地址是:" << &m_d << endl;
}
void func1() { cout << "m_d=" << m_d << endl; }
};
int main()
{
cout << "基类占用内存的大小是:" << sizeof(A) << endl;
cout << "派生类占用内存的大小是:" << sizeof(B) << endl;
B *p=new B;
p->func(); p->func1();
// memset(p, 0, sizeof(B));
*((int*)p + 2) = 31; // 把基类私有成员m_c的值修改成31。
p->func(); p->func1();
delete p;
}