C++对象模型剖析(六)一一Function语义学(一)

Function语义学(一)

C++支持三种类型的 function:

  • nonstatic member function
  • static member function
  • virtual member funcion

Member 的各种调用方式

  • Nonstatic Member Funcions非静态成员函数

    C++的设计准则之一就是:**nonstatic member funcion 至少必须和一般的 nonmember funcion 有相同的效率。**也就是说调用 nonstatic member function 不应该带来额外的负担。

    float magnitude3d ( const Point3d *_this ) { ... }
    float Point3d::magnitude3d () const { ... }// 假设magnitude3d的定义
    float magnitude3d ( const Point3d *_this)
    {return sqrt( _this->_x * _this->_x + _this->_y * _this->_y +_this->_z * _this->_z );
    }
    

    直接这样看到话,好像非静态成员函数更加有效率,因为它们省去了寻址的过程。但是,实际上,非静态成员函数在编译器内部就是转化为了非成员函数的定义。我们看看编译器是如何将非静态成员函数进行转化的吧。

    1. 改写函数签名(signature,书上叫做函数原型,但是我觉得叫函数签名比较好听,因为很多其他书上也是这么叫的),并安插一个额外的参数到成员函数中,用以提供一个管道,使 class object 得以将此函数调用。该额外参数称为 this 指针。(执行期,this指针会自动地跟一个class object 进行绑定)。

      Point3d
      Point3d::magnitude(Point3d *const this) { ... }
      Point3d
      Point3d::magnitude(const Point3d *const this) { ... }
      
    2. 将每一个 “ 对 nonstatic data member 的存取操作” 改为经由 this 指针来存取。

      {return sqrt(this->_x * this->_x +this->_y * this->_y +this->_z * this->_z);
      }
      
    3. 将 member function 重新写成一个外部函数。该函数名经过 “ mangling ” 处理,使它在程序中成为独一无二的语汇:

      extern magitude__7Point3dFv (register Point3d *const this);
      
    4. 将每一个调用操作进行转换

      obj.magintude();
      magnitude__7Point3dFv(&obj);
      ptr->magintude();
      magintude__7Point3dFv(ptr);
      

    我们看看当返回值有类型的时候是怎么重写的,其实这在之前的章节中都有提到。

    Point3d Point3d::normalize()
    {register float mag = magnitude();Point3d normal;normal._x = _x/mag;normal._y = _y/mag;normal._z = _z/mag;return normal;
    }float Point3d::magnitude() const
    {return sqrt(_x * _x + _y * _y + _z * _z);
    }// 编译器内部转化
    void normalize_7Point3dFv( register const Point3d *const this, Point3d &__result)
    {register float mag = this->magnitude();// default constructor_result.Point3d::Poind3d();_result._x = this->_x/mag;_result._y = this->_y/mag;_result._z = this->_z/mag;return;
    }
    

    现在我们看看的书上说的一个比较有效率的方法(但是我没看出来,可能是因为不太了解构造函数吧哈哈,也可能是构造函数里面用到了是初值化列表initialize table

    Point3d Point3d::normalize() const
    {register float mag = magnitude();return Point3d(_x/mag, _y/mag, _z/mag);
    }// 看看编译器是怎么进行内部转换的
    void
    normalize_7Point3dFv( register const Point3d *const this,Point3d &_result)
    {register float mag = this->magnitude();_result.Point3d::Point3d(_x/mag, _y/mag, _z/mag);return;
    }
    

    书:这可以节省默认构造函数所引起的负担(有道理)。

  • 名称的特殊处理 (Name Mangling)

    一般而言,member 的名称前面会被加上 class 名称,形成独一无二的命名。

    class Bar { public: int ival; ... }
    // 经过 name mangling 之后
    int ival_3Bar;
    

    为什么这么做呢?看个例子就清楚了。

    class Foo : public Bar { public: int ival; ...}
    // 现在在一个Foo类中,有两个同名的变量,编译器需要对它们进行区分
    // 我们对不同类变量的区分是这样的
    int Foo::ival;
    int Bar::ival;
    // 但是编译器往往采用最简单的方法--改名-> name mangling
    class Foo : public Bar
    {
    public:int ival_3Foo;int ival_3Bar;
    }
    

    众所周知,类成员包括成员变量和成员函数,现在我们就来看看成员函数。

    由于函数可以被重载化,所以编译器需要更广泛的 mangliing 手法。

    看例子:

    class Point {
    public:void x( float newX);float x();...
    };// 如果转换为
    class Point {
    public:void x_5Point(float newX);float x_5Point();...
    }
    

    这样的话就会有两个函数拥有相同的名字,但是一般情况下,也是不会出错的,因为它们的函数签名并不相同。但是为了制造出独一无二的效果,只有将它的参数列表也加进到编码中。

    但是如果你声明了 extren C,就会压抑 nonmember functions 的 “mangling” 效果。

  • virtual menber functions 虚拟成员函数

经过前面的学习,相信大家对 virtual member function 和 virtual function table 都不陌生了吧。但是书上有的该写的还是得写,唉。

// 如果这是个 virtual member function
ptr->normalize();
// 那么在编译器内部就会转化为
(* ptr->vptr[1])(ptr);
  • vptr 表示由编译器产生的指针,指向 virtual function table(通过之前的学习,我们知道,可能 virtual function table 中不一定只有virtual function pointer,可能在负索引是 virtual base class 的 offset)。它被安插在每一个“声明有(或继承自)一个或多个 virtual functions ”的 class object 中。事实上, 它的名称也可能被 “mangling” 因为在一个复杂的 class 派生体系中,可能存在多个 vptr。
  • 1 表示 virtual table slot 的值(由编译器在编译器决定的,关联到对应的函数。
  • 第二个 ptr 表示 this 指针。

现在我们看看之前的例子:如果 normalize()也是一个 virtual member function

// register float mag = magnitude()
register float mag = (* this->vptr[2])(this);

这个时候,由于Point3d::magnitude()是在Point3d::noralize()中被调用的,而后者已经由虚拟机制而决议(resolve)妥当,所以直接显式调用Point3d实例会更有效率,并因此压制了由于虚拟机制而产生的不必要的重复操作:

register float mag = Point3d::magnitude();

如果magnitude()声明为 inline 函数,会更有效率。使用clas scope operator显式调用一个 virtiual function,其决议方式会和 nonstatic member function 一样。

Point3d obj;
obj.normalize();
// change
normalize_7Point3dFv( &obj );
// 并不需要通过 vptr 对 virtual member function 进行访问

书:编译器的这种优化工程的另一个利益是,virtual function 的一个 inline 函数实例可以被扩展开来(expanded),因而提供极大的效率利益。(后面会讲到)

  • Static Member Functions (静态成员函数)

    在引入 Static Member Function 之前,C++语言要求所有的 member function 都必须经由该 class 的 object 调用。而实际上,只有当一个或多个 nonstatic data members 在member function 中被直接存取时,才需要 class object。Class object 提供了 this 指针给这种形式的函数调用使用。这个 this 指针把 “在 member function 中存取的 nonstatic class members” 绑定于 “object 内对应的 members ” 之上。如果没有任何一个 members 被直接存取,事实上就不需要 this 指针,因此也就没有必要通过一个 class object 来调用一个 member function。但是C++语言到目前为止并不能辨识这种情况。

    这么一来就在存取 static data members 产生了一些不规则性。如果 class 的设计者把 static data member 声明为 nopublic(好习惯),那么他就必须提供一个或多个 member function 来存取该 member。因此,虽然你可以不靠 class object 来存取一个 static member,但是其存取函数必须绑定一个 class object。

    所以。。。

    下面介绍一个 static member function 的特性:

    • 它不能够直接存取其 class 中的 nonstatic members。
    • 它不能够被声明为 const,volatile 或 virtual。
    • 它不需要经由 class object 才被调用。

    下面再看看一些需要注意的小细节

    // 先把例子定义好
    unsigned int Point::object_count ()
    {return _object_count;
    }
    
    1. 首先,static member function 也是会被 mangling 的。

    2. 对一个 static member function 取地址,得到的类型是一个 nonmember function pointer,而不是一个 class member function pointer。

      auto p = &Point3d::object_count();
      // p -> unsigned int (*)()
      // 而不是 
      // unsigned int (Point3d::*)()
      
    3. 由于缺乏 this 指针,因此差不多等同于 nonmember function。所以,它可以成为一个 callback 函数(回调函数)。

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

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

相关文章

云手机运行在云端?安全性有保障吗

随着云计算技术的不断发展,云手机作为一种新兴的移动终端形态,逐渐成为人们关注的焦点。然而,对于许多人来说,云手机 是一个相对陌生的概念,安全性成为了他们最为关心的问题之一。本文将就云手机运行在云端的特点以及其…

SDL基础使用07(YUV数据显示)

SDL显示YUV数据和文件 使用SDL播放随机yuv数据 #include <stdio.h> #include <Windows.h>extern "C" { #include <SDL.h> }#pragma comment(lib, "SDL2.lib")/* 填充随机颜色 */ static void FillYuvImage(BYTE* pYuv, int nWidth, i…

Neo4j 新手教程 环境安装 基础增删改查 python链接 常用操作 纯新手向

Neo4j安装教程&#x1f680; 目前在学习知识图谱的相关内容&#xff0c;在图数据库中最有名的就是Neo4j,为了降低入门难度&#xff0c;不被网上很多华丽呼哨的Cypher命令吓退&#xff0c;故分享出该文档&#xff0c;为自己手动总结&#xff0c;包括安装环境&#xff0c;增删改查…

华为Web举例:私网用户通过三元组NAT访问Internet

Web举例&#xff1a;私网用户通过三元组NAT访问Internet 介绍私网用户通过三元组NAT访问Internet的配置举例。 组网需求 某公司在网络边界处部署了FW作为安全网关。为了使私网中10.1.1.0/24网段的用户可以正常访问Internet&#xff0c;需要在FW上配置源NAT策略。除了公网接口…

修改表中某个字段等于另一个字段减去 2 小时的 SQL

需求&#xff1a;将表中到达时间按照客户要求改为比赛时间的提前 N 小时&#xff0c;具体如下&#xff1a; 表结构 update contestSchedule SET mainRefereeArrivalTimeDATE_FORMAT(CONCAT(2024-03-04 ,gameTime)- INTERVAL 2 HOUR, %H:%i), assistantRefereeArrivalTimeDAT…

【Unity】UnityWebRequest time out 0 bytes received问题

关键词&#xff1a;UnityWebRequest、Http协议、Get请求、0 bytes received using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking;public class MyWebRequest : MonoBehavi…

2024关于idea激活码报This license xxxx has been suspended

HOSTS文件中增加 0.0.0.0 www.jetbrains.com 0.0.0.0 account.jetbrains.com 然后

代码随想录 二叉树第五周

目录 235.二叉搜索树的最近公共祖先 701.二叉搜索树的插入操作 450.删除二叉搜索树中的节点 669.修建二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 235.二叉搜索树的最近公共祖先 235. 二叉搜索树的最近公共祖先 中等 给定一个二叉搜索树,…

浅谈MySQL 索引

MySQL 索引类型 1&#xff1a;主键索引 索引列中的值必须是唯一的&#xff0c;不允许有空值。 2&#xff1a;普通索引 MySQL中基本索引类型&#xff0c;没有什么限制&#xff0c;允许在定义索引的列中插入重复值和空值。 3&#xff1a;唯一索引 索引列中的值必须是唯一的&…

App前端开发跨平台框架比较:React Native、Flutter、Xamarin等

引言 移动应用开发领域的跨平台框架正在不断演进&#xff0c;为开发者提供更多选择。在本文中&#xff0c;我们将比较几个流行的跨平台框架&#xff1a;React Native、Flutter和Xamarin等。讨论它们的优缺点、适用场景以及开发体验。 第一部分 React Native: 优缺点、适用场景…

Spring MVC BeanNameUrlHandlerMapping原理解析

在Spring MVC框架中&#xff0c;路由机制是实现请求到处理器映射的核心。其中&#xff0c;BeanNameUrlHandlerMapping是Spring MVC提供的一种基于bean名称的URL映射策略。本文将详细解析BeanNameUrlHandlerMapping的工作原理、特点、使用场景以及配置和使用方法。 一、BeanNam…

uniapp实现单选框卡片选择器,支持微信小程序、H5等多端

采用uniapp-vue3实现的一款单选框卡片选择器&#xff0c;纯CSS实现样式和交互&#xff0c;提供丝滑的动画选中效果&#xff0c;支持不同主题配置&#xff0c;适配多端 可到插件市场下载尝试&#xff1a; https://ext.dcloud.net.cn/plugin?id16901 使用示例 示例代码 <te…

Linux操作系统项目上传Github代码仓库指南

文章目录 1 创建SSH key2.本地git的用户名和邮箱设置3.测试连接4.创建仓库5.终端项目上传 1 创建SSH key 1.登录github官网,点击个人头像,点击Settings,然后点击SSH and GPG keys,再点击New SSH key。 Title 可以随便取&#xff0c;但是 key 需要通过终端生成。 Linux终端执行…

运用Tensorflow进行目标检测

对象检测是一种计算机视觉技术&#xff0c;它使软件系统能够从给定的图像或视频中检测、定位并跟踪物体。对象检测的一个特殊属性是它能识别对象的类别&#xff08;如人、桌子、椅子等&#xff09;并在给定图像中指出其具体位置坐标。这个位置通常通过在物体周围绘制一个边界框…

探究java反射取值与方法取值性能对比

探究java反射取值与方法取值性能对比 由于我开发框架时&#xff0c;经常需要对象取值。常用的取值方式有&#xff1a; 反射取值方法调用取值 环境 同一台电脑&#xff1a; jdk 21.0.2 idea 2023.3.3 1. 测试代码&#xff08;常用&#xff09; 1.1 反射取值 public stat…

u-boot增加自定义命令

0、说明 本文基于U-Boot 2022.01-v2.07版本进行分析。 1、u-boot编译流程简要分析 2、u-boot启动流程简要分析 3、u-boot增加自定义命令 3.1、u-boot命令行实现简要分析 1&#xff09;cli_init命令行初始化 cli_init定义在common\cli.c中&#xff1a;void cli_init(void) {…

C++基础入门 --- 练习案例【1-10】

文章目录 C基础入门 --- 练习案例1.三只小猪称体重2.猜数字3.水仙花数4.敲桌子5.乘法口诀表6.五只小猪称体重7.数组元素逆置8.考试成绩统计9.冒泡排序10.结构体数组排序 C基础入门 — 练习案例 1.三只小猪称体重 说明&#xff1a;有三只小猪分别为A、B、C,分别输入三只小猪的…

【Web】浅浅地聊JDBC java.sql.Driver的SPI后门

目录 SPI定义 SPI核心方法和类 最简单的SPIdemo演示 回顾JCBC基本流程 为什么JDBC要有SPI JDBC java.sql.Driver后门利用与验证 SPI定义 SPI&#xff1a; Service Provider Interface 官方定义&#xff1a; 直译过来是服务提供者接口&#xff0c;学名为服务发现机制 它通…

acme.sh申请ssl免费证书

参考 https://blog.csdn.net/fyhju1/article/details/120452141 获取域名服务商AccessKey ID及AccessKey Secret https://help.aliyun.com/zh/ram/user-guide/create-an-accesskey-pair 安装ACME curl https://get.acme.sh | sh source ~/.bashrc如果使用root用户进行安装&…

如何在windows上像linux的ssh一样远程访问其它windows

主要分成两部分&#xff1a; 1. 如何远程执行指令 使用psexec&#xff0c;示例如下&#xff1a; PsExec64.exe \\远程计算机ip -u 用户名 -p 密码 -i cmd.exe 这样你就能连接到远程计算机上执行命令了&#xff0c;效果如下 2. 如何远程拷贝文件 分成两步&#xff1a; net…