深度探索C++对象模型 个人总结 第四章 Function语意学

Function语意学

static member functions不能直接存取nonstatic数据,不能被声明为const

4.1Member的各种调用方式

Nonstatic Member Functions(非静态成员函数)

nonstatic member function至少必须和一般的nonmember function有相同的效率,这是因为编译器内部已将”member函数实例“转换为对等的”nonmember函数实例“

转换步骤:

  1.改写函数的signature(译注:意指函数原型)以安插一个额外的参数到member function中,用以提供一个存取管道,使class object得以将此函数调用。该额外参数被称为this指针

  2.将每一个”对nonstatic data member的存取操作“改为经由this指针来存取

  3.将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指针,下面的特性都是根源于其主要特性:

  1.它不能够直接存取其class中的nonstatic members

  2.它不能够被声明为const,volatile,virtual

  3.不需要经由class object才被调用

foo().static_member_function();
//会被转换为

(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实例,需要知道:

  1.ptr所指对象的真实类型。这可使我们选择正确的virtual function实例(一个字符串或数字,表示class的类型)

  2.virtual function实例的位置,以便能够调用它(一个指针,指向某表格,表格中持有程序的virtual functions的执行期地址,地址和地址指向的表格在执行期都是固定不变的)

为了找到函数地址,每个virtual function被指派一个表格索引值

一个class只会有一个virtual table。每一个table内含其对应之class object中所有active virtual functions函数实例的地址:

  1.这一class所定义的函数实例。它会改写一个可能存在的base class virtual function函数实例

  2.继承自base class的函数实例。这是在derived class决定不改写virtual function时才会出现的情况

  3.一个pure_virtual_called()函数实例,它既可以扮演pure virtual function的空间保卫者角色,也可以当作执行期异常处理函数

单一继承

单一继承的三种可能性:

  1. 将base class virtual table中的地址拷贝到derived class virtual table的相对应slot之中
  2. 可以使用自己的函数实例(被放到对应slot之中)
  3. 可以加入新的virtual function, 就增加virtual table尺寸, 新的函数实例地址被放入该slot之中

从上面可以看到, 单继承下的虚函数效率高, 实现简单, 但是多继承和虚拟继承则要复杂很多

多重继承下的Virtual Functions

和第三章相似,多重继承中支持virtual functions的复杂度主要围绕在第二个及其后继的base classes身上,以及“必须在执行期调整this指针”

“Derived 支持virtual functions”的困难度

  1.virtual destructor

  2.被继承下来的class virtual function

  3.一组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供应了三种风味:

  1.一个单一继承实例

  2.一个多重继承实例

  3.一个虚拟继承实例

“指向Member Functions之指针”的效率

4.5Inline Functions

在下面的情况下, 一个函数是inline函数:

  1.声明中包含inline关键字的函数

  2.当一个函数(成员函数或非成员友元函数)的定义在类内部时

  3.被声明为constexpr的函数(since C++11)

关键词inline只是一个请求,并不能强制将任何函数都变成inline,处理一个inline函数有两个阶段:

  1.分析函数定义,以决定函数的“intrinsic inline ability”(本质的inline能力)

  2.真正的inline函数扩展操作是在调用时编译器决定是否“不可为inline”

形式参数

在inline扩展期间,每一个形式参数都会被对应的实际参数取代。而面对可能对实际参数多次求值操作则会引入临时性对象

局部变量

如果inline函数以单一表达式扩展多次,则每次扩展都需要自己的一组局部变量,如果以分离的多个式子,那么只需一组局部变量

inline是对C程序中大量使用的#define的一个安全替代品——特别是宏中的参数有副作用的话。但inline函数如果被调用太多次的话会产生大量的扩展码,使程序大小暴涨

原文地址:https://www.cnblogs.com/GodZhuan/p/14191160.html