上篇C++57个入门知识点_17 类的访问权限及C语言模拟类的封装(类的私有权限突破方法:编译期进行权限检查,运行期通过指针修改类的私有成员变量;利用函数指针对结构体中成员变量进行修改;CPU大小尾排列)讲述了类中成员的访问权限,使用C语言模拟类的封装,本篇将会介绍C++中十分重要的概念-this指针。
个人总结:
- this的指向请铭记:
谁创建的就指向谁,谁在使用成员函数就指向谁
- 非静态成员函数实际上的形参个数比程序员写的多一个(参考上篇C的模拟封装),多出来的参数就是所谓的“this指针”。
这个“this指针”指向了成员函数作用的对象,在成员函数执行的过程中,正是通过“this指针”才能找到对象所在的地址,因而也就能找到对象的所有非静态成员变量的地址。
摘自:this指针理解
静态成员函数并不作用于某个对象,所以在其内部不能使用 this 指针;否则,这个 this 指针该指向哪个对象呢?
参考学习视频地址:this指针
本篇总结:
1.类的大小及成员函数性质:
(1)类对象的数据是独立的:类的大小通常就指的是成员变量的大小
(2)类对象的成员函数是共用的:同一个类的对象,其成员函数地址是一样的,表示同一个类的对象的成员函数是共用的
2.this指针: 因为类的成员函数时对象共用的,为了区分出是哪个对象调用函数,所以在里面偷偷藏了叫做this指针的东西,用于指向使用成员函数的对象。
(1)this指针中存放的是指向对象的指针,对象的首地址;
(2)静态成员函数先于对象存在,是所有对象所共有的,没有this
3.类的成员函数指针写法及调用:
//定义类的成员函数指针
typedef int(CClock::*PFN_GETHOUR)(void);
//当为对象时的写法
CClock clock;
PFN_GETHOUR lp = &CClock::getHour;
//调用
(clock.*lp)();
//当为指针时的写法
CClock clock;
PFN_GETHOUR lp = &CClock::getHour;
//调用
(clock->*lp)();
1 类的大小及成员函数性质
首先讲一下类的大小、及类里面的成员及函数的相关知识点。
(1)类对象的数据是独立的:类的大小通常就指的是成员变量的大小
(2)类对象的成员函数是共用的:同一个类的对象,其成员函数地址是一样的,表示同一个类的对象的成员函数是共用的
1.1 类的大小
上篇中我们介绍过以下clock类
的大小为12字节
,因为其内部有3个int类型数据
,每个int为4个字节
。
#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 getHour()
{
return 1;
}
};
int main(int argc, char* argv[])
{
CClock clock;
int nSize = sizeof(clock);
return 0;
}
F10
单步调试结果:可以看到clock对象
的大小为12字节
那自然而然的就有个疑问了,类里不仅有数据还有函数,12个字节是数据的大小,那函数到哪去了呢?占不占用空间呢?
1.2 类成员函数性质
1.2.1 类成员函数地址及指针
从函数指针的角度来查看,函数具有函数指针,类里具有很多的成员函数,给这些函数的指针起名为类成员函数指针
,参考C语言基础入门48篇_38_函数指针与typedef(函数指针即指向函数的指针、函数指针定义int (*pfn)(int)、typedef给数据类型起别名,起到的作用一致、typedef简化函数指针)。
类成员函数指针的写法PFN_GETHOUR lp = &CClock::getHour;
,&CClock::getHour
表示取类域成员函数地址,并赋给类成员函数指针。
//定义类的成员函数指针
typedef int(CClock::*PFN_GETHOUR)(void);
int main(int argc, char* argv[])
{
CClock clock;
PFN_GETHOUR lp = &CClock::getHour;
int nSize = sizeof(clock);
return 0;
}
此处可以看出,类成员函数指针并未指定对象就可以获得地址,类的成员函数好像跟对象是没有关系的
。
1.2.2 类成员函数是类的对象所共有的
我们同时定义两个clock对象,下断点
#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 getHour()
{
return 1;
}
};
//定义类的成员函数指针
typedef int(CClock::*PFN_GETHOUR)(void);
int main(int argc, char* argv[])
{
CClock clock;
CClock clock2;
clock.getHour();
clock2.getHour();
PFN_GETHOUR lp = &CClock::getHour;
//类成员函数指针的调用方法
(clock.*lp)();
int nSize = sizeof(clock);
return 0;
}
-
在
clock对象的getHour()处
设置断点,跳入函数内部
进入函数的汇编模式,查看到函数的地址为“00CD1F30”
(如何进入汇编,请查看博客内的其他文章)。
-
运行至
clock2
时,同样进入汇编模式下,可以看到getHour()
的地址仍为“00CD1F30”
。 -
此时再查看两个对象的成员变量:
每个对象都有自己的成员变量,分别存储在不同的区域
这就说明:两个对象对应的成员函数是共用的,而类里面成员变量是私有的
。
2. this指针
因为类的成员函数时对象共用的,为了区分出是哪个对象调用函数,所以在里面偷偷藏了叫做this指针的东西,用于指向使用成员函数的对象。
成员函数调用时会偷偷的传递this指针,通过寄存器ecx传递,这种传递方式称为thiscall
(1)this指针中存放的是指向对象的指针,对象的首地址;
(2)静态成员函数先于对象存在,是所有对象所共有的,没有this
此时就有个疑问,如果像如下去写,赋值会如何赋值呢
int main(int argc, char* argv[])
{
CClock clock;
CClock clock2;
clock.SetHour(111);
clock2.SetHour(222);
return 0;
}
运行结果:
虽然不同对象调用的是同一个函数,但是函数知道是哪个对象调用的,这是为什么呢?
从上章中利用C语言封装类中可以看到为了修改对象中的变量的值将其对象的指针传入函数: void SetHour(struct taglock* c1,int n) {c1->nHour = n;}
。因为对象指针的存在,也就知道是哪个对象了。
因此 clock.SetHour(111); clock2.SetHour(222);
表面上看只传递了数据111
或者222
,但是函数内部有一个this指针
的东西,this指针就代表了当前的对象是谁。
因此函数中也可以写为以下形式:m_nHour是成员变量,属于某个对象,this指针代表了当前对象
void SetHour(int nHour)
{
if (nHour >= 24 && nHour < 0)
{
return;
}
this->m_nHour = nHour;
};
运行结果:this地址
即为当前对象地址
this指针传递原理:(没搞懂)
//类域成员函数的地址即为类域成员函数指针
//定义类的成员函数指针(__thiscall系统内部默认的关键字意为类的成员函数的调用约定)
//__thiscall通过ecx寄存器传参
typeid int(CClock::*PFN_GETHOUR)(void)
//&CClock::GetHour意类域成员函数地址,即为类域加成员函数取地址
PFN_GETHOUR lp=&CClock::GetHour
通过寄存器偷偷的将this指针传递进来,所以在函数中可以看到this指针
3. VS中设置启动项
设置启动项,当VS中打开多个项目之后,可以通过设置“设为启动项目”
,调试启动的即为想要处理的项目。
4.学习视频地址:C++57个入门知识点_18_ 类的大小+成员函数性质+this指针