浅谈C++的this指针
之所以写这篇文章,主要是为了回答网友 zhancaihua123同学的下面几个问题:
father* p=new son;
p->disp(...);
father是父类,son是子类。disp是一个子类重定义过的虚函数。
问题一:p->disp(...);是不是可以写成p->disp(p,...);
问题二:p的类型是不是father*
问题三:子类disp函数的this指针是不是son*类型的
问题四:如果第三的问题回答“是”请回答第四题,回答“不是”的,请回避!
那么如果disp函数的this指针是 son*类型的,用p调用disp的时候,我们将p的值传递给disp的this指针,那么既然p是father*类型的,而this是son*类型的,则上述操作就相当于把父类的指针转换为子类指针,这不是与父类指针不能自动(隐式)转换为子类指针矛盾么?
看回答,发现有好多人this指针的本质有错误的认识,估计不少人一说起this指针,脑袋立即反应出:那个类里使用的this指针,那么这个指针就是那个类类型咯。其实事实根本不是这样子的,这里修正对this指针的错误理解:
首先,zhancaihua123同学,我在这里重申一点:“this”不是变量,是关键字,意味着this指针并不是哪个真实存在的符号/储存空间。所以,this指针没有C++语言范畴里的变量类型。所以我曾说的“子类disp函数的this指针是son*类型”这个说法是错的。this指针即不能说是son*类型也不能说是"father"类型。
当然,可能不少同学因此反驳,我在IDE里敲入“this->”后会自动弹出类的成员变量啊,怎么就说this没有类型呢?确实,this指针多多少少跟所在类的类型有关。因此,我也对this指针使用“类型”一词,但是这里“类型”包含下面的两层含义:
- 起始地址(严格来说应该是0偏移地址)。
- 涵盖范围/寻址范围。
因为在机器层面,变量(其实压根就没有什么变量,该叫操作数)除了整型、浮点型之分外,是没有类型的,更没有类成员一说,只有寻址。据此,再进一步说明之前先介绍两个基本常识:(一下全部在X86-64机器VC++编译器范围类讨论)
======================================基本常识分界线=====================================
基本常识1:
类成员是如何访问的?(注:没有特别说明,都不包括静态成员)
当C++被编译成二进制代码后,不考虑什么导出符号、未定义符号之类的,什么变量名就统统消失,那么要访问这些变量,就得通过机器层面的方式:如以某个地址作为标准/基准(起始地址),通过距离这个基准一定量来定位要访问的变量。对于类成员来说,一般是对象首地址+偏移。比如以下这个类:
class A
{
int a;
int b;
virtual void F();
}
他的一个对象aa在内存中的分布如下:(为什么起始地址为F0H?没有为什么,随便定的)
那么假设对象aa的首地址(F0H)保存在寄存器rax中,那么成员a的访问方式为:
rax+8h
同理,成员b的访问方式:
rax+Ch
基本常识2:
什么是继承?
比如如下定义:
struct IA
{
int a;
virtual void F();
void FF();
};
struct B
{
int b;
virtual void G()=;
};
class Derived:public B,public IA
{
public:
int d;
virtual void F();
void FF();
virtual void G();
};
那么假设有Derived d,d对象的内存分布图如下:
好了,简介完两个基本常识,开始讲this指针的“类型”。
========================================this指针的类型=================================
this指针跟不少人想象的不一样,它的类型由被调用函数决定。它的类型遵循着这两点规则:(途中打勾为首地址。)
1.对于非虚函数,this指针的基准地址为函数定义所在层级对象的首地址,范围为该层级对象始末。
如:IA::FF();
this指针类型是以D0H为首地址,范围是从首地址开始到DFH为止。(其实里面有内存空洞,我们不去纠结这个)
Derived::FF();
this指针类型是C0H为首地址,范围是从首地址到E8H为止。
2.对于虚函数,this指针的基准地址为函数首先声明者的首地址,范围为实现者的始末。
如:IA::F();
其this指针类型是以D0H为首地址,范围是从首地址开始到DFH为止。
Derived::F();
其this指针类型是以D0H为首地址,范围是从C0H到E8H为止。
那么当有IA* a=new Derived();后,
a->F();便是这么访问b成员的了。假设首地址D0H保存在寄存器rax里,
rax-8
========================================结案陈词======================================
好了,这里可以最后总结回答原问题三及其新问的“如果用对象指针调用函数,那么到底是指针传递给this还是&object传递给this?”
之所以有 zhancaihua123同学所疑惑的“this指针到底是father*还是son*”,秘密就在这里。之所以觉得它像是father*,是因为形如IA* a=new Derived();a所存放的地址是D0H,通过a所能直接访问的范围限于上面的橙色区域,并且当调用F()时,所传的地址、this指针的值就是a的值D0H。而又觉得它是son*,是因为在F()内部,通过this指针可以访问的范围值整个子类对象。于是乎就让人觉得有父类指针隐式转换为子类指针之嫌。
顺便解答了 zhancaihua123同学第二个问题,就是传给this指针的肯定是a的值即对象指针的值。
=========================================鸣谢========================================
有人说,提出好的问题,等于解决了问题的一半。有些问题,我们没留意到,没认真想过,所以通常都是想当然。正是 zhancaihua123同学所发问,引起我的思考,才去写代码检验自己的想法。所以,最该感谢的,就是提出问题的zhancaihua123同学。
浅谈C++的this指针的更多相关文章
- 浅谈C中的指针和数组(一)
本文转载地址:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html 在原文的基础上加入自己的想法作为修改. 指针是C/C ...
- 浅谈c语言的指针
对于非计算机专业的同学,c语言的指针往往就是老师的一句“指针不考“就带过了.c语言的指针号称是c语言的灵魂,是c语言中最精妙的部分. 指针本质上也是变量,也就是一段内存,只是他的特殊之处是他存储的数据 ...
- 浅谈 “空指针、野指针、void*”
Author: JW. Zhou Date: 2014/7/2 一.空指针(0/NULL) 返回NULL和返回0是完全等价的,因为NULL和0都表示空指针,换句话说:空指针是什么,就是 ...
- 浅谈C中的指针和数组(七)
现在到揭露数组名本质的时候了,先给出三个结论: (1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组: (2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量: ( ...
- 浅谈C中的指针和数组(六)
数组和指针,原本不想在写了,觉得这部分差不多了,但是自己在写程序的时候还是发现了一个错误.首先说一下我的要求: 给一个函数传递一个二维数组,然后我想在这个函数里面计算这个数组的行数. 写个类似的错误D ...
- 浅谈C中的指针和数组(五)
前面写了一些C指针和数组的一些知识,但是还有一些很重要的知识没有交代,这里做一个补充. 首先看一下,普通变量(指针也是变量)和数组名查看地址的方式是不同的. 查看数组变量的地址,不需要使用 & ...
- 浅谈C中的指针和数组(四)
原文转载地址:http://see.xidian.edu.cn/cpp/html/476.html 在原文的基础上增加自己的思想作为自己的修改 指针数组和数组指针的内存布局 初学者总是分不出指针数组与 ...
- 浅谈C中的指针和数组(二)
原文转载地址:http://see.xidian.edu.cn/cpp/html/475.html 在原文的基础上增加自己的想法作为修改 很多初学者弄不清指针和数组到底有什么样的关系.我现在就告诉你: ...
- 浅谈C中的指针和数组(三)
上一个博客我们得到了一个结论: 指针和数组根本就是两个完全不一样的东西.只是它们都可以“以指针形式”或“以下标形式”进行访问.一个是完全的匿名访问,一个是典型的具名+匿名访问.一定要注意的是这个“以X ...
随机推荐
- 2013年的一些常用PHP資源整理下載
這些資源基本上都是一些免積分或只需要1個積分就能下載的資源,經過整理篩選最後分享給大家,希望大家喜歡: PHP手册(chm) http://download.csdn.net/detail/u0118 ...
- 汇编语言学习笔记(5)——[bx]和loop
1.[bx]代表将bx寄存器中的值作为偏移地址. 2.loop与循环有关 3.inc bx的含义为bx中的内容+1 4.loop指令的格式为: loop 标号 CPU运行loop指令的时候.要进行两步 ...
- Codeforces Round #384 (Div. 2) A. Vladik and flights 水题
A. Vladik and flights 题目链接 http://codeforces.com/contest/743/problem/A 题面 Vladik is a competitive pr ...
- 你可能不知道的python
1.如何循环获得下标,使用 enumerate ints = ['a','b','c','d','e','f'] for idx, val in enumerate(ints): print idx, ...
- ecshop利用.htaccess实现301重定向的方法
实现方法如下(空间必须支持对目录中的.htaccess文件解析) 打开 .htaccess 找到 RewriteEngine on 它的下方添加 RewriteCond %{HTTP_HOST} ^需 ...
- MyEclipse设置编码方式
1.windows->Preferences……打开"首选项"对话框,左侧导航树,导航到general->Workspace, 右侧Text file encoding ...
- Carthage 安装和使用
和Cocoapods相比各有利弊吧,具体对比参见: Carthage 初探:四大优势与四大劣势 第一步:如果没有安装Homebrew先安装 打开命令终端,直接输入以下命令回车 /usr/bin/rub ...
- android dalvik heap 浅析
android 系统中可以在/system/build.prop中配置dalvik堆的有关设定.具体设定由如下三个属性来控制 -dalvik.vm.heapstartsize 堆分配的初始大小,调整 ...
- vs.net 2005 C# WinForm GroupBOX 的BUG?尝试读取或写入受保护的内存。这通常指示其他内存已损坏
其实很久没有写程序了,国庆难得有空闲,写了个游戏辅助机器人,程序写好能用后本想把UI控件放到GroupBox里归下分类,美化下界面,结果一运行报“尝试读取或写入受保护的内存.这通常指示其他内存已损坏” ...
- 获得 LayoutInflater 实例的三种方式
在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById().不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例 ...