接上篇:C++57个入门知识点_16 类的标准写法(类名、成员变量、成员函数及对象命名规则;成员变量一般为私有,成员函数为公有并暴露给外部使用成员变量;防止类过大,声明写在.h,实现写在.cpp,调用.h)。前面两篇介绍了面向对象的语言的特点、一个类应该怎样去写、访问权限,本篇将会讲 类的访问权限及C语言模拟类的封装。
总结:
1.类的访问权限及突破方法:
(1) 类的访问权限检查是在编译器进行的;在编译时期对访问权限进行限制,在程序运行时期可以进行修改;
(2)在运行期间可以通过指针修改内存值:*(int*)&clock = 111;
2. 利用函数指针在C语言中模拟类的封装: 利用函数指针typedef void((*PFN_SetHour)(int n));
对结构体中的成员变量进行修改(可以用来感悟成员函数是属于整个类的)
1. 类的访问权限及突破方法
类的访问权限检查是在编译器进行的;在编译时期对访问权限进行限制,在程序运行时期可以进行修改
。
前面已知private
只有在类域内可以访问,我们要讨论一个问题,访问的权限有没有办法突破呢?
下面为上篇中的代码,我们已经知道clock.m_nHour=1;在编译时是无法通过的,但是当程序运行之后,对象
clock`是存在于内存中的,因此是否可以操作内存来修改对象的私有成员变量呢?
#include <iostream>
class CClock {
private:
int m_nHour;
int m_nMinute;
int m_n_Second;
public:
void SetHour(int nHour)
{
if (nHour >= 24 && nHour < 0)
{
return;
}
m_nHour = nHour;
};
};
int main(int argc, char* argv[])
{
CClock clock;
clock.m_nHour=1;
return 0;
}
1.1 通过调试器修改内存值
下面的代码给类的私有成员变量赋初值,通过内存去查看对象中成员变量的值,并通过调试器对内存中得值进行修改。
#include <iostream>
class CClock {
private:
int m_nHour=1;
int m_nMinute=2;
int m_n_Second=3;
public:
void SetHour(int nHour)
{
if (nHour >= 24 && nHour < 0)
{
return;
}
m_nHour = nHour;
};
};
int main(int argc, char* argv[])
{
CClock clock;
return 0;
}
运行之后:内存中可以看到,私有变量具有初始值
1.2 CPU内存中大小尾排列方式
大家可能看到了,在内存中m_nHour
的值是按照01 00 00 00
排列的,而不是我们在二进制排列00 00 00 01
,这是由于CPU的大小尾
的关系相关,上面显示的是Intel CPU的方式。
大尾方式 即低地址数据在低位,高地址数据在高位(低低高高的排列方式),01 00 00 00
四个数据对应地址分别为0x004FFA50 0x004FFA51 0x004FFA52 0x004FFA53
1.3 运行中通过指针修改内存值
在程序运行时,我们可以通过调试器修改内存中值。
那么我们是否可以代码方式在程序运行时修改内存,应该也是可以的。这样做的前提时你需要知道类在内存中占用多少字节?
类中包含3个int类型数据,但是类除了数据之外,还会有对齐值等影响类在内存中占用的字节数,此处不要求掌握。
一种简单的方法,使用int nSize = sizeof(clock);
来编译时查看变量等在内存中占用的字节数。
对代码修改后可以看到,clock对象占用字节数为12,且m_nHour
所在地址为首地址。
突破访问权限的方法:
//如何突破类的限制访问类的成员变量?常规语法不可以,可以通过指针的方式进行访问
//左侧含义,&clock取对象地址,(int*)&clock将地址强制准换为int指针,*(int*)&clock取指针下的内容
*(int*)&clock = 111;
运行结果如下:对象clock中的私有成员变量在程序运行时改变
2. 利用函数指针在C语言中模拟类的封装
此处另外介绍一个小的知识点,我们通过C语言的方式,对手表进行封装,利用C语言模拟类的封装。C语言中结构体中是不可以增加行为的,但是我们又想将函数放入其中,如果放不了函数,我们可以将函数当做一种数据放入结构体中,数据即一种函数指针的方式
。
(1) 函数指针的定义方式: typedef void((*PFN_SetHour)(int n));
(2) 使用真实的函数地址给函数指针赋值方法: c1.pfnSetHour = SetHour;
(3) 函数指针的调用: c1.pfnSetHour(1);
(4) 为了修改对象中的变量的值将其对象的指针传入函数: void SetHour(struct taglock* c1,int n) {c1->nHour = n;}
(5) 修改结构体的变量值的方法: c1.pfnSetHour(&c1,1);
C++中编译器已经有了c1对象,相当于少传了一个&c1指针。
C语言中封装方法如下:
//封装:
#include <iostream>
//定义函数指针,相当于变量,需要赋值
typedef void((*PFN_SetHour)(int n));
//C语言模拟类的封装
struct taglock {
int nHour;
int nMinute;
int n_Second;
PFN_SetHour pfnSetHour;
};
//通过struct taglock* c1传入需要修改的目标,与结构体关联
void SetHour(struct taglock* c1,int n) {
c1->nHour = n;
}
//调用函数
main()
{
struct taglock c1;
c1.pfnSetHour = SetHour;
//类似于类中成员函数的调用
c1.pfnSetHour(&c1,1);
}
上面的代码为伪代码的形式,可以用于理解,以下是我进行修改后的可以运行的代码:
//封装:
#include <iostream>
//结构体的前置声明,给函数指针中使用
struct taglock;
//定义函数指针,相当于变量,需要赋值
typedef void((*PFN_SetHour)(taglock*,int n));
//C语言模拟类的封装
struct taglock {
int nHour;
int nMinute;
int n_Second;
PFN_SetHour pfnSetHour;
};
//通过struct taglock* c1传入需要修改的目标,与结构体关联
void SetHour(struct taglock* c1, int n) {
c1->nHour = n;
}
//调用函数
void main()
{
struct taglock c1;
c1.pfnSetHour = SetHour;
//类似于类中成员函数的调用
c1.pfnSetHour(&c1, 1);
}
3.视频学习地址:C++57个入门知识点_17 类的访问权限及C语言模拟类的封装