Function语意学

C++ Function语意学 1.Function语意学 假设有一个Point3d的指针和对象: Point3d obj; Point3d *ptr = &obj; 这一章主要讨论,在进行如下调用时,会发生什么: obj.normalize(); ptr->normalize(); //normalize函数的定义 Point3d Point3d::normalize() const { register float mag = magnitude(); Point3d normal; norm 阅读详情


本文主要介绍C++对象中成员函数有关内容,详细示例请参考C++对象成员函数存储示例。

一、成员函数各种调用方式

考虑如下所示的代码段:
在这里插入图片描述

C++支持三种类型的成员函数:static、nonstatic、virtual,每一种类型被调用的方式都不相同。

1.1、非静态成员函数

C++的设计准则之一就是:非静态成员函数至少必须和一般的非成员函数有相同的效率。例如,对于如下所示的两个函数:
在这里插入图片描述
选择成员函数不应该带来什么额外负担,这是因为编译器内部已将"成员函数实例"转换为对等的"非成员函数实例"

1.1.1、转换函数

转换步骤分为三步:

  • 1、改写函数的signature以安插一个额外的参数到成员函数中,用以提供一个存取管道,使类对象可以调用此函数。这个额外的参数被称为this指针:
    在这里插入图片描述
    如果成员函数是const的,则变成:
    在这里插入图片描述
  • 2、将每一个"对非静态数据成员的存取操作"改为经由this指针来存取:
    在这里插入图片描述
  • 3、将成员函数重新写成一个外部函数。将函数名称经过"mangling"处理,使它在程序中成为独一无二的语汇:
    在这里插入图片描述

1.1.2、转换调用

现在这个函数已经被转换好了,而其每一个调用操作也都必须转换。如下所示:
在这里插入图片描述
对于一开始所示的代码段,normalize()函数将会被转换为下面的形式。其中假设已经声明有一个Point3d copy constructor,而named returned value(NRV)的优化也已实行:
在这里插入图片描述
一个比较有效率的做法是直接构建"normal"值,像这样:
在这里插入图片描述
在这里插入图片描述

1.1.3、名称的特殊处理

1.1.3.1、数据成员

一般而言,成员的名称前面会加上类名称,形成独一无二的命名。例如下面的声明:
在这里插入图片描述
其中的ival有可能变成这样:
在这里插入图片描述
为什么编译器要这么做?请考虑这样的派生操作:
在这里插入图片描述
有可能被转换为:
在这里插入图片描述
现在,无论要处理哪一个ival,通过"name mangling",都可以绝对清楚地指出来。

1.1.3.2、成员函数

由于成员函数可以被重载,所以需要更广泛的mangling操作,以提供绝对独一无二的名称。如果把:
在这里插入图片描述
转换为:
在这里插入图片描述
会导致两个被重载的函数实例拥有相同的名称。为了让它们独一无二,唯有再加上它们的参数列表。如果把参数类型也编码进去,就一定可以制造出独一无二的结果:
在这里插入图片描述

1.2、虚成员函数

1.2、虚成员函数

本节只介绍简单的虚成员函数情况,有关继承机制的内容请参看二、虚成员函数。

如果normalize()是一个虚成员函数,那么下面的调用:
在这里插入图片描述
将会被内部转换为:
在这里插入图片描述
其中:

  • vptr表示由编译器产生的指针,指向virtual table。它被安插在每一个声明有(或继承自)一个或多个虚函数的类对象中。事实上其名称也会被"mangled",因为在一个复杂的类派生体系中,可能存在多个vptr。
  • 1是virtual table slot的索引值,关联到normalize()函数
  • 第二个ptr表示this指针

类似的道理,如果magnitude()也是一个虚函数,它在normalize()之中的调用操作将被转换为:
在这里插入图片描述
此时,由于Point3d::magnitude()是在Point3d::normalize()中被调用的,而后者已经由虚拟机制而决议妥当,所以显式地调用"Point3d实例"会比较有效率,并因此压制由于虚拟机制而产生的不必要的重复调用操作:
在这里插入图片描述
如果magnitude()声明为inline函数,会更有效率。使用类范围操作符(::)显式调用一个虚函数,其决议方式会和非静态成员函数一样:
在这里插入图片描述

1.3、静态成员函数

如果Point3d::normalize()是一个静态成员函数,以下两个调用操作:
在这里插入图片描述
将会被转换为一般的非成员函数调用,像这样:
在这里插入图片描述

1.3.1、特性

静态成员函数的主要特性就是它没有this指针。以下的次要特性统统根源于其主要特性:

  • 它不能够直接存取其类中的非静态成员
  • 它不能够被声明为const、volatile或virtual
  • 它不需要经由类对象即可被调用——虽然大部分时候它是这样被调用的!

二、虚成员函数

二、虚成员函数

虚函数的一般实现模型为:每一个类有一个virtual table,内含该类中有作用的虚函数的地址,每个对象有一个vptr,指向virtual table,如1.2、虚成员函数节所示,本节主要介绍有关继承的各种情况。

2.1、单一继承下的虚函数

2.1.1、必要的信息及其存放位置

为了支持虚函数机制,必须首先能够对于多态对象有某种形式的"执行期类型判断法(runtime type resolution)"。也就是说对于调用操作:ptr->z(),将需要ptr在执行期的某些相关信息,如此一来才能够找到并调用z()的适当实例。

或许最直接了当但是成本最高的解决办法就是把必要的信息加在ptr身上。在这样的策略之下,一个指针(或一个引用)持有两项信息:

  • 1、它所参考到的对象的地址(也就是目前它所持有的东西)
  • 2、对象类型的某种编码,或是某个结构(内含某些信息,用以正确决议出z()函数实例)的地址

这个方法带来两个问题:第一,它明显增加了空间负担,即使程序并不使用多态;第二,它打断了与C程序间的链接兼容性。

如果这份额外信息不能够和指针放在一起,下一个可以考虑的地方就是把它放在对象本身。接下来考虑两个问题:1、什么时候需要这些信息呢?2、哪些对象真正需要这些信息呢?

  • 对于第一个问题,很明显是在必须支持某种形式的"执行期多态(runtime polymorphism)"的时候。

  • 对于第二个问题,由于没有导入像是polymorphic之类的新关键词,因此识别一个类是否支持多态,唯一适当的方法就是看看它是否有任何虚函数。只要类拥有一个虚函数,它就需要这份额外的执行期信息。

2.1.2、构建virtual table

下一个明显的问题是,我们需要存储什么样的额外信息?也就是说,对于调用操作:ptr->z() ,其中z()是一个虚函数,那么需要什么样的信息才能让我们在执行期调用正确的z()实例?我们需要知道:

  • ptr所指对象的真实类型,这可使我们选择正确的z()实例
  • z() 实例的位置,以便我们能够调用它

在实现上,首先我们可以在每一个多态的类对象身上增加两个成员:

  • 1、一个字符串或数字,表示类的类型
  • 2、一个指针,指向某表格,表格中持有程序的虚函数的执行期地址

表格中的虚函数地址如何被构建起来?在C++中,虚函数(可经由其对象被调用)可以在编译时期获知。此外,这一组地址是固定不变的,执行期不可能新增或替换之。由于程序执行时,表格的大小和内容都不会改变,所以其构建和存取皆可以由编译器完全掌控,不需要执行期的任何介入。

然而,执行期备妥那些函数地址,只解决了问题的一半。另一半是找到那些地址。两个步骤可以完成这项任务:

  • 1、为了找到表格,每一个类对象被安插了一个由编译器内部产生的指针,指向该表格
  • 2、为了找到函数地址,每一个虚函数被指派一个表格索引值

这些工作都由编译器完成。执行期要做的,只是在特定的virtual table slot中激活虚函数。

一个类只会有一个virtual table。每一个virtual table内含其对应类对象中所有虚函数实例的地址。这些虚函数包括:

  • 这一类所定义的函数实例。它会覆盖一个可能存在的基类虚函数实例
  • 继承自基类的函数实例。这是在派生类决定不改写虚函数时才会出现的情况
  • 一个pure_virtual_called()函数实例。它既可以扮演纯虚函数的空间保卫者角色,也可以当做是执行期异常处理函数(有时候会用到)

每一个虚函数都被指派一个固定的索引值,这个索引在整个继承体系中保持与特定的虚函数的关系。

2.1.3、示例

对于下面的Point类:
在这里插入图片描述
virtual destructor被指派slot1,而mult()被指派slot2,。此例并没有mult()的函数定义,所以pure_virtual_called()的函数地址会被放在slot2中。如果该函数意外地被调用,通常的操作是结束掉这个程序。y()被指派slot3,而z()被指派slot4。x()的slot是多少?答案是没有,因为x()并非虚函数。Point的内存布局和其virtual table如下所示:
在这里插入图片描述

当一个类派生自Point时,会发生什么?例如class Point2d:
在这里插入图片描述
一共有三种可能性:

  • 1、它可以继承基类所声明的虚函数的函数实例。正确地说是,该函数实例的地址会被拷贝到派生类的virtual table的相对应slot中
  • 2、它可以使用自己的函数实例。这表示它自己的函数实例地址必须放在对象的slot中
  • 3、它可以加入一个新的虚函数。这时候virtual table的尺寸会增大一个slot,而新的函数实例地址会被放进该slot中

Point2d的virtual table在slot1中指出destructor,而在slot2中指出mult()(取代纯虚函数)。它自己的y()函数实例地址放在slot3中,继承自Point的z()函数实例地址则放在slot4中。Point2d的内存布局和其virtual table如下所示:
在这里插入图片描述

类似的情况,Point3d派生自Point2d,如下:
在这里插入图片描述
其virtual table中的slot1放置Point3d的destructor,slot2放置mult()函数地址,slot3放置继承自Point2d的y()函数地址,slot4放置自己的z()函数地址。Point3d的内存布局和其virtual table如下所示:
在这里插入图片描述
对于ptr->z() ,我们如何有足够的知识在编译时期设定虚函数的调用呢?

  • 一般而言,在每次调用z()时,我们并不知道prt所指对象的真正类型。然而,经由ptr可以存取到该对象的virtual table
  • 虽然我们不知道哪一个z()函数实例会被调用,但我们知道每一个z()函数地址都被放在slot4中

这些信息使得编译器可以将调用ptr->z() 转换为:
在这里插入图片描述
在这一转换中,vptr表示编译器所安插的指针,指向virtual table;4表示z()被指派的slot编号。唯一一个在执行期才能知道的东西是:slot4所指的到底是哪一个z()函数实例。

在一个单一继承体系中,虚函数机制的行为十分良好,不但有效率而且很容易塑造出模型来。

2.2、多重继承下的虚函数

在多重继承中支持虚函数,其复杂度围绕在第二个及后继的基类身上,以及"必须在执行期调整this指针"这一点。以下面的类体系为例:
在这里插入图片描述

2.2.1、三个问题

派生类支持虚函数的困难度,统统落在Base2 subobject身上。有三个问题需要解决,以此例而言分别是(1)virtual destructor,(2)被继承下来的Base2::mumble(),(3)一组clone()函数实例。

2.2.1.1、问题一

首先,我们把一个从heap中配置而得的派生类对象的地址,指定给一个Base2指针:
在这里插入图片描述
新的派生类对象的地址必须调整以指向其Base2 subobject。编译时期会产生以下的代码:
在这里插入图片描述
如果没有这样的调整,指针的任何"非多态运用"(像下面那样)都将失败:
在这里插入图片描述
当要删除pbase2所指的对象时:
在这里插入图片描述
指针必须被再一次调整,以求再一次指向派生类对象的起始处。然而上述的offset加法不能够在编译时期直接设定,因为pbase2所指的真正对象只有在执行期才能确定。

一般规则是,经由指向"第二或后继基类"的指针(或引用)来调用派生类的虚函数。其所连带的必要的"this指针调整"操作,必须在执行期完成。也就是说,offset的大小,以及把offset加到this指针上的那一小段程序代码,必须由编译器在某个地方插入。问题是,在哪个地方?

Bjarne原先实施于cfront编译器中的方法是将virtual table加大,使它容纳此处所需的this指针,调整相关事物。每一个virtual table slot,不再是一个指针,而是一个集合体,内含可能的offset以及地址。于是虚函数的调用操作由:
在这里插入图片描述
改变为:
在这里插入图片描述
这个做法的缺点是,它相当与连坐处罚了所有的虚函数调用操作,不管它们是否需要offset的调整。

比较有效率的解决方法是利用所谓的thunk。所谓thunk是一小段assemble代码,用来(1)以适当的offset值调整this指针,(2)跳到虚函数去。例如,经由一个Base2指针调用派生类析构函数,其相关的thunk可能看起来是这个样子:
在这里插入图片描述
Bjarne并不是不知道thunk技术,问题是thunk只有以assembly代码完成才有效率可言。由于cfront使用C作为其程序代码产生语言,所以无法提供一个有效率的thunk编译器。

thunk技术允许virtual table slot继续内含一个简单的指针,因此多重继承不需要任何空间上的额外负担。slots中的地址可以直接指向虚函数,也可以指向一个相关的thunk(如果需要调整this指针的话)。

调整this指针的第二个额外负担就是,由于两种不同的可能:(1)经由派生类(或第一个基类)调用,(2)经由第二个(或其后继)基类调用,同一函数在virtual table中可能需要多笔对应的slots。例如:
在这里插入图片描述
虽然两个delete操作导致相同的派生类destructor,但它们需要两个不同的virtual table slots:

  • 1、pbase1不需要调整this指针(因为Base1是最左端的基类,所以它已经指向派生类对象的起始处)。其virtual table slot需放置真正的destructor地址
  • 2、pbase2需要调整this指针。其virtual table slot需要相关的thunk地址。

在多重继承之下,一个派生类内含n-1个额外的virtual table,n表示其上一层基类的个数(因此,单一继承将不会有额外的virtual table)。对于本例的派生类而言,会有两个virtual table被编译器产生出来:

  • 1、一个主要实例,与Base1(最左端基类)共享
  • 2、一个次要实例,与Base2(第二个基类)有关

针对每一个virtual table,派生类对象中有对应的vptr。如下所示:
在这里插入图片描述
有三种情况,第二或后继的基类会影响对虚函数的支持。第一种情况是,通过一个指向第二个基类的指针,调用派生类虚函数。即上述讨论的virtual destructor情况,例如:
在这里插入图片描述
从上图可以看到这个调用操作的重点:ptr指向派生类对象中的Base2 subobject;为了能够正确执行,ptr必须调整指向派生类对象的起始处。

2.2.1.2、问题二

第二种情况是第一种情况的变体,通过一个指向派生类的指针,调用第二个基类中一个继承而来的虚函数。在此情况下,派生类指针必须调整,以指向第二个base subobject。例如:
在这里插入图片描述

2.2.1.3、问题三

第三种情况发生于一个语言扩充性质之下:允许一个虚函数的返回值类型有所变化,可能是基类类型,也可能是派生类类型。以Derived::clone()函数为例。clone函数的派生类版本传回一个派生类指针,默默地改写了它的两个基类函数实例。当我们通过指向第二个基类的指针来调用clone()时,this指针的offset问题于是诞生了:
在这里插入图片描述
当执行pb1->clone()时,pb1会被调整指向派生类对象的起始地址,于是clone()的派生类版本会被调用;它会传回一个指针,指向一个新的派生类对象;该对象的地址在被指定给pb2之前,必须先经过调整,以指向Base2 subobject。

2.3、虚继承下的虚函数

考虑下面的虚基类派生体系,从Point2d派生出Point3d:
在这里插入图片描述
虽然Point3d有唯一一个(同时也是最左边的)基类,也就是Point2d,但Point3d和Point2d的起始部分并不像非虚拟的单一继承情况那样。这种情况如下所示:
在这里插入图片描述
当一个虚基类从另一个虚基类派生而来,而且两者都支持虚函数和非静态数据成员时,编译器对于虚基类的支持简直就像进了迷宫一样。因此作者建议我们:不要在一个虚基类中声明非静态数据成员。

三、指向成员函数的指针

取一个非静态成员函数的地址,如果该函数是nonvirtual,得到的结果是它在内存中真正的地址。然而这个地址是不完全的。它需要被绑定于某个类对象的地址上,才能够通过它调用该函数。所有的非静态成员函数都需要对象的地址(以参数this指出)。

使用一个成员函数指针,如果并不用于虚函数、多重继承、虚基类等情况的话,并不会比使用一个非成员函数指针的成本更高。

3.1、指向虚成员函数的指针

考虑下面的代码:
在这里插入图片描述
pmf是一个指向成员函数的指针,被初始化为Point:: z()(一个虚函数)的地址。prt则被指定一个Point3d对象。如果我们直接经由ptr调用z():
在这里插入图片描述
被调用的是Point3d:: z()。但是如果我们从pmf间接调用z()呢?
在这里插入图片描述
虚拟机制仍然能够在使用指向成员函数的指针情况下运行。问题是如何实现的呢?

对一个非静态成员函数取其地址,将获得该函数在内存中的地址。然而面对一个虚函数,其地址在编译时期是未知的,所能知道的仅是虚函数在其相关的virtual table中的索引值。也就是说,对一个虚成员函数取其地址,所能获得的只是一个索引值。

以下面的Point声明为例:
在这里插入图片描述
然后取destructor的地址:
在这里插入图片描述
得到的结果是1。取x()或y()的地址:
在这里插入图片描述
得到的则是函数在内存中的地址,因为它们不是虚函数。取z()的地址:
在这里插入图片描述
得到的结果是2。通过pmf来调用z(),会被内部转换为一个编译时期的式子,一般形式如下:
在这里插入图片描述
对一个指向成员函数的指针评估求值(evaluated),会因为该值有两种意义而复杂化:既可以指向虚函数,也可以指向非虚函数;其调用操作也将有别于常规调用操作。pmf的内部定义是:
在这里插入图片描述
必须允许此函数能够寻址出非虚函数x()和虚函数z()两个成员函数,而那两个函数有着相同的原型:
在这里插入图片描述
只不过其中一个代表内存地址,另一个代表virtual table中的索引值。因此,编译器必须定义pmf,使它能够(1)持有两种数值,(2)更重要的是其数值可以被区别代表内存地址还是virtual table中的索引值。

在cfront2.0非正式版中,这两个值被内含在一个普通的指针内。cfront使用了以下技巧:
在这里插入图片描述
这种实现技巧必须假设继承体系中最多只有128个虚函数。这并不是我们所希望的,但却证明是可行的。然而,多重继承的引入,导致需要更一般化的实现模式,并趁机去除对虚函数的个数限制。

3.2、多重继承下,指向成员函数的指针

为了让指向成员函数的指针能够支持多重继承和虚继承,Bjarne设计了下面一个结构体:
在这里插入图片描述
index和faddr分别(不同时)持有virtual table索引和非虚成员函数地址(为了方便,当index不指向virtual table时,会被设为-1)。在此模型之下,像这样的调用操作:
在这里插入图片描述
会变成:
在这里插入图片描述

四、总结

在这里插入图片描述

VSCode里玩转AvaloniaUI:手把手教你搭建跨平台桌面应用开发环境(附.NET 7配置避坑) 本文详细介绍了如何在VSCode中搭建AvaloniaUI跨平台桌面应用开发环境,包括.NET 7配置避坑指南。从环境准备、项目创建到调试优化,手把手教你利用VSCode和Avalonia实现高效开发,特别解决了.NET 7依赖等常见问题,助力开发者快速上手跨平台应用开发。 阅读详情

相关推荐

emqx Java 订阅以及发送 结合若依框架

emqx Java 订阅以及发送 结合若依框架

Spo的博客 1613

【C++】成员函数

C++成员函数(Member Functions) 目录 C++成员函数(Member Functions) 1、成员函数定义(Member Function Definition) 2、内联(Inline) 3、常成员函数(Const Member Functions) 4、使用对象指针(Using Object Pointer) 5、重载成员函数(Overloading Mem...

Alvin的博客 2万+

实战篇【2】-H3C防火墙开局基础配置

本篇文章为“H3C防火墙开局基础配置”,主要涵盖配置管理地址、聚合端口、安全区域、安全策略、静态路由、ACL,WEB和SSH远程管理、Console、SNMP、NTP时钟、日志主机等内容,对比交换机配置开局,增加两处配置,安全区域和安全策略。方便调试人员远程管理、调试。

weixin_47402139的博客 1万+

【C++】类的常见成员函数

int _month;int _day;d1.Print();Date d2;d2.Print();return 0;对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置 信息,未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?

lyhv_v的博客 2422

STM32F407+LWIP实战:NETCONN接口TCP服务器避坑指南(附正点原子例程解析)

本文深入解析了基于STM32F407和LWIP协议栈构建高响应TCP服务器的实战要点。针对使用NETCONN接口时常见的连接不稳定、数据延迟及内存泄漏等问题,提供了从NETCONN运行模型、服务器实现、参数优化到内存管理的全方位避坑指南,并结合正点原子例程,给出了具体的调试技巧与解决方案。

weixin_29188043的博客 402

【免费】最新区块链钱包和私钥的助记词碰撞器,使用python开发

最新钱包私钥助记词碰撞器,搏一搏单车变摩托!

weixin_48529185的博客 1万+

C++程序设计——类的六个成员函数

C++程序设计:类的六个默认成员函数

m0_63020222的博客 4398

别再只会插拔了!深入Linux内核,手把手调试USB热插拔中断(以高通平台为例)

本文深入探讨了Linux内核中USB热插拔中断的调试方法,特别针对高通平台提供了详细的实战指南。从硬件基础到内核机制,再到中断调试工具和常见问题排查,帮助开发者系统性地解决USB设备识别问题。通过监控中断状态、分析处理流程和高级调试技巧,提升USB热插拔的稳定性和性能。

weixin_29161785的博客 428

C++入门之类与对象(中)——默认成员函数

详细讲解了C++的类中的六大默认成员函数

Masquerena114514的博客 1838

汽车ECU诊断入门:手把手教你用UDS协议(ISO14229)读取和清除故障码

本文详细介绍了如何使用UDS协议(ISO14229)进行汽车ECU诊断,包括故障码读取与清除的完整流程。从诊断环境搭建、UDS协议基础到实战案例分析,手把手教你掌握0x19诊断服务、安全访问机制等核心技能,适用于汽车电子工程师和诊断开发人员。

weixin_33701251的博客 453

从零构建简历智能匹配系统:AI技术助力高效招聘

本文详细介绍了简历智能匹配系统的开发过程,从数据收集、预处理、特征提取到模型匹配和结果展示,每一步都提供了详细的代码实例。通过使用TF-IDF、Word2Vec和BERT等技术,我们能够有效地将求职者的简历与职位描述进行匹配,从而提高招聘效率。未来,随着自然语言处理技术的进一步发展,简历智能匹配系统将变得更加智能和高效,为企业和求职者提供更好的服务。

理论都是虚的,代码才是王道。 309

java总结 第四章 认识成员函数

一、定义和使用成员函数 1、最简单的成员函数 最简单的成员函数的格式如下: 注意:在类的内部,普通的成员函数可以直接使用同一个类中的成员变量,不需要加对象名。 2、加入参数的成员函数 加入参数的成员函数格式如下: 3、带返回类型的成员函数 该函数格式如下: 注意:(1)“int abs(int a)"定义了函数abs,返回一个整数类型的值。 (2)”int result=c.abs(-10);"表示调用该函数,将返回值...

mycamelllia的博客 2232

西门子G120变频器在立体仓库堆垛机应用参数设置

本文详细介绍了G120 CU240E及CU250S变频器的参数设置流程。主要内容包括:1)快速调试步骤,从新建项目、扫描节点到设置电机类型、负载类型等参数;2)抱闸参数设置方法;3)参数下载异常时的处理方案。重点说明了通过STARTER软件进行向导式调试的完整过程,包括电机铭牌参数输入、识别方式选择等关键环节,并提示了通讯连接注意事项。当下载失败时,提供了通过导入导出项目文件解决问题的具体方法。

xdpcxq的专栏 863

Function 语意学

在前面文章《C++对象模型》可以知道,class 中成员函数的类型有:nonstatic member function、static member function、virtual member function;不同类型的成员函数的调用会有不同的表现,以下对每一种类型的成员函数进行简单的分析。

关注校招求职,微信号:job_campus 1259

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C++对象模型:Function 语意学

使用C++时,成员函数和非成员函数在性能上应该是等价的。当设计类时,我们不应该因为担心效率问题而避免使用成员函数。实现:编译器会将成员函数转换为一个带有额外this指针参数的非成员函数,这使得成员函数可以直接访问对象的数据成员。成员函数到非成员函数的转换签名修改:首先,成员函数的签名会被修改,在最前面添加一个隐式的this指针作为参数。如果成员函数是const的,那么this指针也会是const。

_ 1056

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

function语意学和析构函数语意学

Point3d obj;Point3d *ptr = &obj;下面的区别:obj.normalize();ptr->normalize();

爱冒险的技术宅 2151

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

第4章 Function语意学

目录 第4章 Function语意学 4.1 Member的各种调用方式 Nonstatic Member Function(非静态成员函数) virtual Member Function(虚成员函数) static member function(静态成员函数) ...

weixin_30830327的博客 298

Function语意学 Member的各种调用方式

Nonstatic Member Functionsnonstatic member functions和一般的nonmember function有相同的效率。也就是说下边两种函数效率相差不多。float func(const ClassA *this){...}//nonmember function float ClassA::func() const{...}//nonstatic memb

zhangqixiang5449的博客 517

深入探索C++对象模型 之 Function语意学

名称的特殊处理(Name Mangling)如member:class bar{public: int ival; …}class foo :public class bar {public: int ival; …}经处理后:class foo{public:int ival_3bar;int ival_4foo;}如function:class po

梦开始的地方  3188

object model-Function 语意学

0.member的各种调用方式 a.nonstatic member functions(非静态成员函数) 好吧,个人觉得作者自己是非常懂的,但是他讲出来的时候,感觉真的好混乱。通常都是给一个例子,自己体悟,做总结的那些话,总是不放在最显眼的位置。让人抓不到重点。歪果仁都是这么随性吗,讲到哪儿是哪儿 辨析member function 和nonmember fun

脚踏实地,仰望星空 653

第四章:function 语意学

第四章:function 语意学 1、与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数和静态数据成员 1)静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;  2)非静态成员函数可以任意地访问静态成员函数和静态数据成员; 

艰难困苦如同欢乐,终将成为人生最后的财富 437

第4章 Function 语意学1: Member的各种调用方式

C++ 的设计准则之一就是:nonstatic member function 至少必须和一般的nonmember function 有相同的效率。那么选择 member function 不应该带来什么额外负担。这是因为编译器内部已将member 函数实体”转换为对等的“nonmember 函数实体。现在这个函数已经被转换好了,而其每一个调用操作也都必须转换。

qq_40178082的博客 856
上一篇: C++对象成员函数存储示例
下一篇: 构造函数语意学
Spring_24
博客等级 码龄12年 46粉丝 41原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Spring_24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值