深度探索C++对象模型 个人总结 第四章 Function语意学
Function语意学
static member functions不能直接存取nonstatic数据,不能被声明为const
4.1Member的各种调用方式
Nonstatic Member Functions(非静态成员函数)
nonstatic member function至少必须和一般的nonmember function有相同的效率,这是因为编译器内部已将"member函数实例"转换为对等的"nonmember函数实例"
转换步骤:
- 改写函数的signature(译注:意指函数原型)以安插一个额外的参数到member function中,用以提供一个存取管道,使class object得以将此函数调用。该额外参数被称为this指针
- 将每一个"对nonstatic data member的存取操作"改为经由this指针来存取
- 将member function重新写成一个外部函数。将函数名称经过"mangling"处理,使它在程序中成为独一无二的词汇
名称的特殊处理(Name Mangling)
member的名称前面加上class名称,再加上它们的参数链表(可以从函数原型参考得到),制造出独一无二的结果
Virtual Member Functions(虚拟成员函数)
virtual member function的调用会被转化为对编译期产生的指向virtual table的指针vptr的访问;并通过索引确定virtual table slot的位置;和非静态成员函数类似,也为其安插一个this指针
Static Member Functions(静态成员函数)
在引入static member functions之前使用
((class*)0)->member function();//将0强制转换为一个class指针,因而提供一个this指针实例
Static member functions的主要特性就是它没有this指针,下面的特性都是根源于其主要特性:
- 它不能够直接存取其class中的nonstatic members
- 它不能够被声明为const,volatile,virtual
- 不需要经由class object才被调用
foo().static_member_function();
//会被转换为<br /><br />
(void)foo();//仍需要评估求值
class::static_member_function();
取一个static member function的地址,指针类型是"nonmember函数指针"
4.2Virtual Member Functions(虚拟成员函数)
多态(Polymorphism)表示"以一个public base class的指针(或reference),寻址出一个derived class object"的意思
识别一个class是否支持多态,唯一适当的方法就是看它是否有任何virtual function。只要class拥有一个virtual function,它就需要一份额外的执行期信息(runtime type identification)
在执行期调用正确的virtual function实例,需要知道:
- ptr所指对象的真实类型。这可使我们选择正确的virtual function实例(一个字符串或数字,表示class的类型)
- virtual function实例的位置,以便能够调用它(一个指针,指向某表格,表格中持有程序的virtual functions的执行期地址,地址和地址指向的表格在执行期都是固定不变的)
为了找到函数地址,每个virtual function被指派一个表格索引值
一个class只会有一个virtual table。每一个table内含其对应之class object中所有active virtual functions函数实例的地址:
- 这一class所定义的函数实例。它会改写一个可能存在的base class virtual function函数实例
- 继承自base class的函数实例。这是在derived class决定不改写virtual function时才会出现的情况
- 一个pure_virtual_called()函数实例,它既可以扮演pure virtual function的空间保卫者角色,也可以当作执行期异常处理函数
单一继承
单一继承的三种可能性:
- 将base class virtual table中的地址拷贝到derived class virtual table的相对应slot之中
- 可以使用自己的函数实例(被放到对应slot之中)
- 可以加入新的virtual function, 就增加virtual table尺寸, 新的函数实例地址被放入该slot之中
多重继承下的Virtual Functions
和第三章相似,多重继承中支持virtual functions的复杂度主要围绕在第二个及其后继的base classes身上,以及"必须在执行期调整this指针"
"Derived 支持virtual functions"的困难度
- virtual destructor
- 被继承下来的class virtual function
- 一组virtual functions实例
将一个derived class object地址指定给一个base class指针,地址必须调整以指向base class subobject;
要删除base class指针时,指针必须被再次调整以指向derived class object的起始处(由于指针所指的真正对象只有在执行期才能确定,所以"this指针调整"也必须在执行期完成)
通过第一个base class指针访问时, 不需要调整this指针。其virtual table slot需放置真正的destructor地址
通过第2,3,...个base class指针访问时, 其virtual table slot需要相关的thunk地址,多重继承下一个derived class内含n-1额外的virtual tables
由于执行期链接器的出现导致函数名称的链接可能变得缓慢,为此将多个virtual tables连锁为一个,通过offset获取指向次要表格的指针
虚拟继承下的Virtual Functions
当一个virtual base class从另一个virtual base class派生而来,并且两者都支持virtual functions和nonstatic data members时,编译器对virtual base class的支持简直就像进了迷宫。所以不要在virtual base class中声明nonstatic data members。
4.3函数的效能
inline函数不只能够节省一半函数调用所带来的额外负担,也提供了程序优化的额外机会
对virtual function的调用,因为调用操作经过了虚拟机制会导致效率降低,而且无论是多重继承还是单一继承都会消耗相同的成本(因为调用)
4.4指向Member Function的指针
指向member function的指针的声明语法,以及指向"member selection 运算符"的指针,其作用是作为this指针的空间保留者,如果该指针不用于virtual function、多重继承、virtual base class等情况,编译器可以为其提供和nonmember function指针相同的效率
支持"指向Virtual Member Functions"的指针
对一个virtual member function取其地址,所能获得的只是一个索引值
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Base1
{
protected:
float data_Base1;
public:
Base1(/* args */) {}
virtual ~Base1() {}
virtual void speakClearly() {}
virtual Base1* clone() const {
return const_cast<Base1*>(this);
}
};
class Base2
{
protected:
float data_Base2;
public:
Base2(/* args */) {}
virtual ~Base2() {}
virtual void memble() {}
virtual Base2* clone() const {
return const_cast<Base2*>(this);
}
};
class Derived :public Base1, public Base2
{
protected:
float data_Derived;
public:
Derived(/* args */) {}
virtual ~Derived() {}
virtual Derived* clone() const {
return const_cast<Derived*>(this);
}
};
int main()
{
void (Base2:: * pmf)()=&Base2::memble;
Base2* pbase2 = new Derived;
(pbase2->*pmf)();
cout << &Base2::memble;
}
在多重继承下,指向Member Functions的指针
Microsoft供应了三种风味:
- 一个单一继承实例
- 一个多重继承实例
- 一个虚拟继承实例
"指向Member Functions之指针"的效率
4.5Inline Functions
在下面的情况下, 一个函数是inline函数:
- 声明中包含inline关键字的函数
- 当一个函数(成员函数或非成员友元函数)的定义在类内部时
- 被声明为constexpr的函数(since C++11)
关键词inline只是一个请求,并不能强制将任何函数都变成inline,处理一个inline函数有两个阶段:
- 分析函数定义,以决定函数的"intrinsic inline ability"(本质的inline能力)
- 真正的inline函数扩展操作是在调用时编译器决定是否"不可为inline"
形式参数
在inline扩展期间,每一个形式参数都会被对应的实际参数取代。而面对可能对实际参数多次求值操作则会引入临时性对象
局部变量
如果inline函数以单一表达式扩展多次,则每次扩展都需要自己的一组局部变量,如果以分离的多个式子,那么只需一组局部变量
inline是对C程序中大量使用的#define的一个安全替代品——特别是宏中的参数有副作用的话。但inline函数如果被调用太多次的话会产生大量的扩展码,使程序大小暴涨
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)