浅谈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 ...
随机推荐
- 小白学数据分析----->DNU/DAU
行业指标观察分析-DNU/DAU 写在分析之前 一直以来,我们对于数据都是在做加法,也希望这个过程中,不断搜罗和变换出来更多的数据指标,维度等等.而在实际的分析中,我们发现,一如我们给用户提供产品一样 ...
- 使用eclipse JDT compile class,解决 无法确定 X 的类型参数;对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最大实例
ant 命令行方式执行build javac编译class出现 泛型无法转换 无法确定 <X>X 的类型参数:对于上限为 X,java.lang.Object 的类型变量 X,不存在唯一最 ...
- 【Android】 TextView设置个别字体样式
SpannableString msp = new SpannableString("测试"+XM+"更换当前号码将从手机发送一条普通短信进行验证"); msp ...
- mysql5.5 uuid做主键与int做主键的性能实测
数据库:mysql5.5 表类型:InnoDB 数据量:100W条 第一种情况: 主键采用uuid 32位. 运行查询语句1:SELECT COUNT(id) FROM test_varchar; 运 ...
- wmi详解,RPC和防火墙
135端口:Microsoft在这个端口运行DCE RPC end-point mapper为它的DCOM服务.这与UNIX 111端口的功能很相似.使用DCOM和RPC的服务利用计算机上的end-p ...
- 测试markdown发布
- [leetcode]Find Minimum in Rotated Sorted Array @ Python
原题地址:https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/ 解题思路:话说leetcode上面的二分查找题目 ...
- Android开发之蓝牙--扫描已经配对的蓝牙设备
一. 什么是蓝牙(Bluetooth)? 1.1 BuleTooth是目前使用最广泛的无线通信协议 1.2 主要针对短距离设备通讯(10m) 1.3 常用于连接耳机,鼠标和移动通讯设备等. 二. ...
- Mac OSX - 如何在bash_profile中配置全局环境变量
我们知道,在Linux操作系统下,配置环境变量是使用如下命令: vim /etc/profile 在OSX下,我们用如下命令打开环境变量配置文件: open ~/.bash_profile 典型的环境 ...
- web项目中加入struts2、spring的支持,并整合两者
Web项目中加入struts2 的支持 在lib下加入strut2的jar包 2. 在web.xml中添加配置 <filter> <filter-name>struts2< ...