深入探讨this指针

YOLOv8交通管理 详细解释及代码完整示例 YOLOv8在交通管理中的应用及代码实现 摘要:本文详细介绍了YOLOv8在交通管理领域的应用场景,包括车辆检测、违规行为识别和行人安全预警等。文章提供了完整的Python代码示例,展示了如何使用YOLOv8进行交通对象检测和车辆计数统计。代码包含模型加载、对象检测、结果可视化等功能模块,并给出了性能优化和功能扩展建议。该实现基于Ultralytics框架,支持多种YOLOv8模型选择,可根据实际需求调整模型大小和检测参数。 阅读详情

深入探讨this指针

 

为了写这篇文章,准备了好长时间,翻遍了箱底的书籍。但是现在还是不敢放开手来写,战战兢兢。不是担心自己写错,而是唯恐自己错误误导别人。同时也希望这篇文章能给你一点收获。既然是深入探讨this指针,所以建议初学者,最好具有一定编译基础,调试基础。如果大家认为这片文章有不满的地方,就给我发信批评一下,以便及时修正。

关于this指针的描述我们一般从语言层次上讲;

this指针作为一个隐含参数传递给非静态成员函数,用以指向该成员函数所属类所定义的对象。当不同的对象调用同一个类的成员函数代码时,编译器会依据该成员函数的this指针所指向的不同对象来确定应该引用哪个对象的数据成员。简单例子

我们定义一个简单stack类

// 定义stack类

class Stack

{

public:

     Stack();// 构造函数

     ~Stack();// 析构函数

public:

     void push(char c);// 压栈函数

private:

     char *top;// 栈顶元素

     char *max;// 栈容量

};

 

// 压栈函数

void Stack::push(char c)

{

     if(top > max)

     {

         ERROR;

     }

     *top++ = c;

}

 

// 定义公共函数,操作栈对象中的push函数

void FunStack(Stack *p)

{

     p->push('c');

}

上面的代码我们加入this概念,以C代码形式显示(你可以理解编译C++成C代码后,Cfront开始就是这么做的)

// 用普通C描述类成员函数

void Stack__push(this,c);// 普通C代码

{

     if(this->top > this->max)

     {

         ERROR;

     }

     *(this->top)++ = c;

}

 

void FunStack(p)// Stack *p;

{

     Stack__push(p,'c');

}

C++中this指针是从Simula(只是听说没有使用过)里的THIS引用的翻版,有时候有人会问,为什么this是指针而不是一个引用?为什么叫this而不是叫self(smalltalk)?第一个问题是,当this引入带类的C时,在那时的是C++中还没有引用机制,所以只能是this指针而不是引用了。第二个问题,更简单了,就是因为this是从simula来,而不是从smalltalk来。

    上面是简单的讨论,我们将逐步深入讨论this。

我们通过this访问对象(已经成惯例了)中函数和变量时一般这样使用

    this->top;// 访问变量

    this->push();// 访问函数

 

    (*this).top;// 访问变量

    (*this).push();// 访问函数

通过上面例子,我们从语言层次上说this是一个指针(也许你说this本来就是一个指针,就叫this指针,不要着急听我慢慢说来)。那么this是一个什么样子的指针,比如我们最常见的指针有。

    int *p;

    Const int *p;

    int * const p;

那么this指针是不是其中一种?下面我们分别验证。

    我们定义类,作为验证对象

    class A

{

public:

     int iData;// 简单期间我们定义为int型

     mutable int iData2;// mutable变量

int Fun1(){return ++iData;};// 普通函数㈠

     int Fun2() const {return ++iData;};// 带const的函数㈡

};

上面的㈠函数可以正确执行。

上面㈡函数,不能通过编译,我们知道在const函数中,不允许修改类中变量。那么最终原因是什么?其实在上面的例子中,我们用C实现

int A_Fun2(const A* this);

const函数本质是const this的原因,所以不允许修改iData值。

至少现在我们可以确定this指针,不是一个const常量指针。因为如果this是常量指针,我们就不能修改类中变量的值了。捎带我们提一下C++中关键字mutable,如上定义的mutable int iData2;// mutable变量,这样我们就可以在const函数中修改iData2的值。其实这时的mutable和public,private,protected是相同的,这些关键字只是在编译时刻有用,编译后变量类型是没有区别的。更深一步说,强制类型转换也是对编译器来说,是通过编译器编译过程中判断类型转换的正误。

    那么this对象是否是A *const this的值哪?首先我们先看一个例子

 

static int iTest = 1;

class A

{

public:

     int iData;// 简单期间我们定义为int型

     mutable int iData2;// mutable变量

     int Fun1()

     {

         int iTemp = 4;

         return ++iData;

     };// 普通函数

     int Fun2()const {return iData;};// 带const的函数

};

 

int _tmain(int argc, _TCHAR* argv[])

{

     A a;

static int iTest1 = 2;

     a.Fun1();

static int iTest2 = 3;

system("pause");

     return 0;

}

我们通过上面的例子查看this的地址,我们定义static对象的目的就是为了用this指针的地址和static变量的地址进行对比,看一看this指针到底分配到哪里?

    注意我们在这里不能直接使用&this获得this的指针,如果我们这样定义会提示

Error C2102 “&”要求一个L值

    通过上面至少我们知道,this不是一个个人定义的变量,只是在运行时刻有效。所以这时如果直接对this取地址,在编译时刻无法通过,提示如上错误。

    既然我们在程序中无法通过&this取得this的地址。那么我们有什么办法取得this的地址?我们上面已经提到this是在运行时刻有效,我们就以据这点查找this的地址。

    为了在取得this的地址,我们使用VC7.0下的命令窗口,在命令窗口中我们使用命令eval,通过这个命令我们可以取得this的地址。我们还是在上面的程序中设置断点

 

在debug下,我们运行上面的程序,并进入断点后,进行取址操作。

>eval &iTest

0x0044afa0 iTest

>eval &iTest1

0x0044afa4 iTest1

>eval &this// 注意只有我们进入Fun1()函数体内才能取得&this的值

0x0012fdf0 "玄_"

>eval &iTest2

0x0044afa8 iTest2

 

通过对比我们可以看出static变量iTest,iTest1,iTest2存放在全局变量区域,而&this(0x0012fdf0)的地址比&iTest(0x0044afa0)地址还要底,而static变量存放在单独全局

区域,并且这个区域是从底地址到高地址递增的。所以通过上面的对比至少我们可以肯定一点this指针的创建要比static变量(或者全局变量)早。那么更比创建A a;对象时调用A的构造函数早,只是创建a对象后,this指向a对象;

当我们创建两个A类对象时,会发现this指针的地址是相同的,但是this指针指向对象不同。当然不同了,如果相同。A a,b;那么a,b对象也就相同了,这种方式肯定是不对的。结论就是同一个类创建多个对象时,多个对象的this指针是同一个指针。也就是说在单进程单线程中this对象在放入CPU寄存器中时都是同一个地址,只是指向不同的对象而已。上面的测试是在DEBUG状态下的测试结果。

那么在Release是什么样?要多亏VC7.0支持Release下的断点,我们在Release下,启动调试。这时需要在Release状态下设置,优化状态为禁用(/Od)

>eval &this CXX0069: 错误: 变量需要堆栈帧

>eval this CXX0069: 错误: 变量需要堆栈帧

>eval *this CXX0069: 错误: 变量需要堆栈帧

 

    在Release状态下&this,this,*this不存在了,提示是变量需要堆栈帧,说明此时的this指针不存在了。难到this指针只是在debug模式下有,在Release模式下没有?而C++语言特性中并没有说this指针在调试状态下有而在Release模式下没有啊?只是强调this指针作为一种隐含参数传递。也就是在正确(请这样理解)的程序中this应该是不存在的,至少可以肯定的是说在内存中不存在this指针。

    我们使用C++的时候知道有一种变量定义方式,也不存放到内存,而是直接放到寄存器中。我想你已经猜到了就是register类型变量,下面我们测试register类型变量是否和this指针是一样的结果。

    在程序中定义:register int iRegData;

    Debug模式下

>eval iRegData

5

>eval &iRegData

0x0012fec4// 注意这个地址,看看是否和>eval &this// 注意只有我们进入Fun1()函数体内才能取得&this的值0x0012fdf0 "玄_"在地址上很接近啊!一个是0x0012fec4,另一个是0x0012fdf0。

    Release模式下

>eval iRegData

5

>eval &iRegData

0x0012fee0

通过上可以知道在debug和Release模式下iRegData都没有直接放入寄存器,而是在内存中开辟了内存空间,至于如何可以在运行时候看出register变量是放到寄存器,而不是内存中,我还不得而知,所以哪位高人知道,麻烦告诉我一声。看来this指针也不是register类型的,或者我现在的能力还不能确定this是register。后来才知道register对编译器只是一个提示,编译器可以执行也可以不执行,就像inline一样。但是至少我们可以使用__inline宏,可以确保函数被inline,但是register?有没有这种策略,我现在还不得而知。

补充:定义变量类型有四中分别是

1:Auto:非static,const类型变量,比如局部变量,int i;char c等。都是auto int i;auto char c;

2:static:静态变量,static int i,static char c;

3:const:常量变量,值不可修改。Const int i,static char c;

4:register:内存变量,编译器把此值直接放入寄存器。Register int i;register char c;

上面讨论我们都是从类中变量进行讨论的,但是无法确定this到底是什么?那么我们继续从类中的函数开始讨论this。并且我们也将逐渐深入编译状态下。

开始的使用已经举了例子,类内函数在解释函数时,把this指针作为函数的第一个参数进行传递。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数(你讲看到this是一个例外)。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。
   
栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作
被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修
改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变
成栈顶,栈顶指针随之修改。

函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。在参数传递中,有两个很重要的问题必须得到明确说明:当参数个数多于一个时,按照什么顺序把参数压入堆栈函数调用后,由谁来把堆栈恢复原装在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:

stdcall
cdecl
fastcall
thiscall
naked call

原来函数调用约定也有这么多啊,看这都有点晕了呵呵。因为这篇文章讲的是this指针,所以在这里我们主要讨论thiscall。

       thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字(所以不要在C++关键字中找了)。它是C++类成员函数缺省的调用约定。由于成员函数调用有一个this指针,因此必须特殊处理,thiscall意味着:参数从右向左入栈,如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈。对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈为了说明这个调用约定,定义如下类和使用代码:

class A
{
public:
int function1(int a,int b);
int function2(int a,...);//
定义VA(可变)函数
};
int A::function1 (int a,int b)
{
return a+b;
}

int A::function2(int a,...)
{
va_list ap;
va_start(ap,a);
int i;
int result = 0;
for(i = 0 i < a i ++)
{
result += va_arg(ap,int);
}
return result;
}
void callee()
{
A a;
a.function1 (1,2);
a.function2(3,1,2,3);
}
callee
函数被翻译成汇编后就变成:
//
函数function1调用
0401C1D push 2
00401C1F push 1
00401C21 lea ecx,[ebp-8]
00401C24 call function1 //
注意,这里this没有被入栈,而是通过ECX传递this指针

此时寄存器的各值如下

EAX = 00000003 EBX = 7FFDF000 ECX = 0012EE43

EDX = 00000001 ESI = 00000000 EDI = 0012EE48

EIP = 0041707A ESP = 0012ED70 EBP = 0012EE48

EFL = 00000206

察看this指针

>eval this

0x0012ee43// 看看这个值是否和ECX相同
//
函数function2调用
00401C29 push 3
00401C2B push 2
00401C2D push 1
00401C2F push 3
00401C31 lea eax,[ebp-8] //
这里引入this指针,并把this指针放入栈内

EAX = 00000006 EBX = 7FFDF000 ECX = 0012ED70

EDX = 00000006 ESI = 00000000 EDI = 0012EE48

EIP = 0041708E ESP = 0012ED70 EBP = 0012EE48

EFL = 00000212

察看this指针

>eval this

0x0012ee43// 看看这个值是否和ECX相同
00401C34 push eax
00401C35 call function2
00401C3A add esp,14h

 

到现在,我们对this得了解还说不上深入了解。简单得说this就是指向对象自身的一个指针,讨论这么多其实就是想了解this在反编译阶段是如何传递运行得。也许就this的了解我们就可以基于以上讨论已经足够了。但是this的应用并不简单的就是这些内容,比如在ATL中,就有专门函数用来保存回复this指针的策略;我们在重载operator=也需要通过this判断赋值等号两边对象,是否指向同一个对象。

 

关于指针:指针和其它变量(int,char等)一样,在声明后会在内存中申请内存空间,存储在在程序的堆栈上,大小一般都是一个机器字的长度(比如在32位机上是4个字节)。简单的说指针是指向内存中地址的变量,可以是数据的地址也可以是函数的地址。一句话:指针是一种用于储存“另外一个变量的地址”的变量。或者拆成两句:指针是一个变量,它的值是另外一个变量的地址。

 

参考资料

孙晓涛等《Windows高级编程》西北工业大学出版社(1997年10月 西安)

逸学堂《关于this指针的深入探讨》优快云

《C++编程思想》

 

 

android 蓝牙通讯demo android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。 立即下载

相关推荐

从USB 2.0到USB 3.x:Synopsys SVT USB VIP配置避坑与接口选择指南

本文详细解析了从USB 2.0到USB 3.x的Synopsys SVT USB VIP配置要点,涵盖协议版本选择、物理接口匹配及UVM层次化架构实现。重点介绍了layering sequence的配置技巧和常见问题解决方案,帮助验证工程师高效避坑并优化验证流程。

weixin_42653691的博客 238

this指针

this是一个指针,它时时刻刻指向你这个实例本身。你可以看到它的成员函数、成员变量,this指针本质上是“成员函数”的形参。你可以看见桌子、椅子、地板等,静态成员函数中不能使用this指针。但是房子你是看不到全貌了。对于一个类的实例来说,当你进入一个房子后,不能给this指针赋值。

m0_63783532的博客 8503

借助OSS搭建在线教育视频课程分享网站-阿里云的课程体验和记录

阿里云的《搭建在线教育网站》免费课程里面讲的啥,我点进去看了看

LegendNoTitle的博客 3023

C++中的This指针

一、This指针的介绍     C++中使用This指针来指向用来调用成员函数的对象。一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函

u012103747的专栏 2880

风力发电机缺陷检测平台

风力发电机缺陷检测平台相关内容展示

weixin_39779415的博客 1111

调试备忘录-SWD协议解析

目录--点击可快速直达 文章目录写在前面1  SWD协议简介2  SWD物理层协议解析2.1  SWD通信时序分析2.2  SWD 寄存器简介2.2.1  DP寄存器2.2.2  AP寄存器2.3  SWD通信流程2.3.1  SWD复位2.3.2  SWD读IDCODE2.3.3  SWD清除错误标志位,并且使能AP调试2.3.4  SWD读取AP IDR(也就是AP寄存器的ID CODE)2.3.5  SWD读写MCU任意寄存器 写在前面 最近由于公司需要,所以就做了个基于SWD协议的离线烧写器。由于.

洛神殇的博客 1万+

this指针详解

我们都知道类的成员函数可以访问类的数据(限定符只是限定于类外的一些操作,类内的一切对于成员函数来说都是透明的),那么成员函数如何知道哪个对象的数据成员要被操作呢,原因在于每个对象都拥有一个指针:this指针,通过this指针来访问自己的地址。注:this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。this指针的类型取决于使用this指针的成员函数类型以及对...

qq_21110983的博客 4910

Blender到Unreal Engine高效资产迁移:Datasmith插件工作流详解

在3D内容创作与实时渲染领域,数字资产的无损迁移是连接创意设计与最终呈现的关键环节。其核心原理在于通过高保真的数据转换管道,替代传统离散的导出-导入流程,从而保留完整的场景层级、材质属性和关联信息。这项技术的核心价值在于极大提升了跨平台工作流的效率与数据保真度,减少了因格式转换导致的信息丢失和调试成本。它广泛应用于建筑可视化、产品设计、影视预演和游戏开发等场景,使艺术家能在专业DCC工具中专注创作,并一键同步至实时引擎进行交互式渲染与逻辑构建。本文聚焦于实现这一流程的**Datasmith**插件,并深入解

weixin_34110970的博客 268

C++基础:this指针的概念,操作及特点

编译器针对程序员自己设计的类型分三次编译。 第一:识别和记录类体中属性的名称,类型和访问限定,与属性在类体中的位置无关。 如:class CGoods中的Name,Amount,Price,Total_value; 第二:识别和记录类体中的函数原型(返回类型+函数名+参数列表),形参的默认值,访问限定。不识别体函数。 第三:改写在类中定义的参数列表和函数体,改写对象调用成员函数的形式。 示例: #include<iostream> using namespace std; clas

weixin_58368590的博客 1921

彻底摆脱文献只能读开头的痛苦!这款文献检索神器真能帮你跨越文献付费限制(附实操演示)

大家是不是有过这样的经历,好不容易通过关键词搜到了和自己主题高度相关的文献,想要快速获取文献信息,推进研究进度,却又因为付费或者需订阅才能下载PDF,而被劝退。粘贴完成后,点击搜索,搜索结果页面会显示这篇文献的相关信息,有的文献有“下载PDF”,有的文献则只有“求助PDF”,不管是哪种情况,直接点击进入;我这里点击的是“下载PDF”,接着它会跳转页面。我用 Book 学术下载过不少付费文献,实测下来 80% 左右的文献,都能通过它的求助方式成功获取到,只有少部分文献等待的时间会稍微久一些或者没办法下载。

qq_64378896的博客 163

光谱数据预处理综合

除了化学信息,光谱还包含其他不相关或不需要的信息,如电噪声,基质背景,杂散光,散射效应等。在用化学计量学方法建立校准模型时,利用预处理方法消除光谱数据中的无关信息和噪声是非常必要的。

qq_44204759的博客 5189

C++语言学习(十二)——C++语言常见函数调用约定

C++语言学习(十二)——C++语言常见函数调用约定 一、C++语言函数调用约定简介 C /C++开发中,程序编译没有问题,但链接的时候报告函数不存在,或程序编译和链接都没有错误,但只要调用库中的函数就会出现堆栈异常等现象。上述现象出现在C和C++的代码混合使用的情况下或在C++程序中使用第三方库(非C++语言开发)的情况下,原因是函数调用约定(Calling Convention)和函数名修饰(...

weixin_34049032的博客 581

Gromacs 模拟昆虫 P450 解毒酶与农药结合完整指南,仅供参考!

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1239

深入探讨this指针:从汇编的角度考虑

深入探讨this指针:从汇编的角度考虑 总结:至此我们对this指针和成员函数调用有了更进一步的认识, (1) this 指针只存在于成员函数内部, (2) 成员函数的调用时,this指针并没有那么神秘,那完全可以认为对象在调用成员函数时将自身地址作为函数的第一个参数在默认情况下传递给了函数的默认形参,只不过这个默认形参被命名为this罢了,仅此而已,小样,不就是穿了一个马甲吗! (3) 尽管我们可以把this指针当做成员函数的第一个默认参数,但是我们心里应该明白,事实上它还是有点特殊待遇的,它与正常的函数

liaoyoujinb的专栏 3452

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: 求解素数几种方法
下一篇: ASCⅡ码表
逸学堂
博客等级 码龄23年 1104粉丝 112原创
评论 14
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值