4.1 Member Function的各种调用方式

一、初探Static Member

已知Point3d类包含三个data members:float _x, _y, _z;和两个member functions: normarlize()和magnitude()。其中,成员函数的的定义如下:

Point3d Point3d::normarlize() const
{
    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);
}

这两个函数可能是virtual函数,也可能是nonstatic function,但这两个函数时静态函数(static member function)吗?一定不是,原因有二:① 它直接存取nonstatic数据_x, _y, _z(static member 存在于class object 之外,位于data segment;static member function中没有this指针);② 它们都被声明为const。static member functions 不能做到这两点。

C++支持三种类型的成员函数:static、nonstatic和virtual,每一种类型被调用的方式都不相同,这一议题将从下节开始。


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

如果上述两个函数不是静态成员函数,而是非静态函数,那么编译器对它们的扩张如下:

Point3d Point3d::normarlize() const
{
    register float mag = magnitude();
    Point3d normal;

    normal._x = _x / mag;
    normal._y = _y / mag;
    normal._z = _z / mag;

    return normal;
}

被转化为:

// 使用C++伪码
void normarlize__7Point3dFv( register const Point3d* const this,
    Point3d &__result)// 返回值的初始化 见2.3节
{
    register float mag = this->magnitude();

    // default constructor
    __result.Point3d();


    __result._x = this->_x / mag;
    __result._y = this->_y / mag;
    __result._z = this->_z / mag;

    return;
}

float Point3d::magnitude() const
{
    return sqrt(_x*_x + _y*_y + _z*_z);
}

被转化为:

// 使用C++伪码
float magnitude__7Point3dFv(const Point3d* const this)
{
    return sqrt(this->_x*this->_x + this->_y*this->_y + this->_z*this->_z);
}

下面介绍编译器是如何对非静态成员函数进行扩张的,转换步骤如下:

1、改写函数的signature(即函数原型:返回值,函数名和函数参数)以安插一个额外的参数——this指针 到member function中,已提供一个存取管道,是class object得以将此函数调用。

如果magnitude为non-const、nonstatic成员,那么扩张为:Point3d Point3d::magnitude(Point3d* const this)。如果成员函数是const,则变成:Point3d Point3d::magnitude(const Point3d* const this)

2、将每一个“对nonstatic data member的存取操作”改为经this指针来存取,当然也可以显式的使用this:return sqrt(this->_x * this->_x + this->_y * this->_y + this->_z * this->_z);

3、将member function重写成一个外部函数,把函数名经过“mangling”处理[见深度探索C++对象模型 P144],使它在程序中成为独一无二的词汇:
extern magnitud __7Point3dFv(register Point3d* const this);


现声明两个Point3d指针和对象,它们的转化如下:

Point3d p3d;
Point3d* p_p3d;
// ...
p3d.magnitude();    // 等价于magnitude__7Point3dFv(&p3d);
p_p3d->magnitude(); // 等价于magnitude__7Point3dFv(p_p3d);

三、Virtual Member Functions(虚拟成员函数)

假设normalize()是一个虚拟成员函数,那么有如下转化

Point3d* p_p3d;
p_p3d->magnitude(); // 等价于(* p_p3d->vptr[1])(p_p3d));

其中:
1、vptr表示由编译器产生的指针,指向virtual table(虚表)。请记住,只要类中含有虚拟成员函数,那么该class object的布局中就一定有一个由编译器产生的指向虚表的指针vptr。其实该vptr也会被“mangled”,因为在一个复杂的class派生体系中,可能存在多个vptrs。

2、1是virtual table slot的索引值,关联到normalize()函数;目前Point3d只有一个虚函数——normalize()函数。

3、第二个p_p3d表示this指针。


类似地,如果magnitude()函数也是一个virtual function,它在normalize()中的调用将被转换为:

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

这里有个提升程序效率的讨论:由于Point3d::normalize()调用了Point3d::magnitude(),而前者已经由虚拟机制而决议妥当,所以显式的调用“Point3d实例”会比较有效率,并因此压制由于虚拟机制而产生的不必要的重复操作:

// 显式的调用(explicitly invocation)会压制虚拟机制
register float mag = Point3d::magnitude();

如果magnitude()被声明为inline函数,会更有效率。

使用“作用域运算符(::)“显式调用virtual function,其决议方式会和nonstatic member function一样:

// register float mag = Point3d::magnitude();
register float mag = magnitude__7Point3dFv(this);

下面介绍关于virtual member function有点奇葩的转换方式,再次强调normalize()为虚拟成员函数。对于以下的调用:

Point3d p3d;
p3d.normalize(); // 转换为什么?

很明显,p3d是一个类对象,而不是指向类对象的指针(如p_p3d)。而我们已经知道那些不支持多态的对象[见1.3节]恰好包含”仅仅是一个类对象“,而指针或引用是发生多态的条件。 如果编译器把它转换为:

(* p3d->vptr[1])(&p3d);

虽然语意正确,却没必要。请记住,我们不必把不支持多态的对象对virtual member function的调用也像支持多态的对象一样享有特殊的待遇(通过虚表关联函数),编译器应该像对待一般的nonstatic member function一样地加以决议:

normalize__7Point3dFv(&p3d);

不支持多态的对象,竟然想享有多态的待遇,我是觉得很奇葩。


四、Static Member Functions(静态成员函数)

又遇到static这个关键词,它为静态的意思。在类中,静态成员(数据成员和成员函数)最大的特性就是没有this指针,你可以这么理解:由于static成员存在于class object之外,存在于data segment,虽然我们在类中定义了static成员函数或数据成员,但它却不属于类(作用域在类对象的生命周期内),比如:你sizeof(class_Name)或sizeof(obj)得到的结果是没有把static member计算进去的。如果理解了static member没有this指针,那么没有this指针的member(数据成员和成员函数)能够调用nonstatic member(数据成员和成员函数)吗?不可以,因为程序设计者写的代码都被编译器转化为带有this指针或class object(使用.调用成员函数)或指向class object的指针(使用->调用成员函数)。

如果Point3d::normalize()是个static member function,由于不是virtual的(它也不可以被声明为虚的)不能使用虚表,由于没有this指针,它也不能使用this指针,又由于static member存在于class object之外;种种原因都可以让你信服以下的两个调用操作:

p3d.normalize();
p_p3d->normalize();

将被转化为nonmember函数调用,像这样:

// p3d.normalize();
normalize__7Point3dSFv(/*除了这里没有this指针,好像和nonstatic member function还挺像,哈哈*/);

// p_p3d->normalize();
normalize__7Point3dSFv(/*除了这里没有this指针,好像和nonstatic member function还挺像,哈哈*/);

static成员的最主要的特性就是它没有this指针,以下的次要特性都根源于其主要特性

1、它不能够直接存取其class中nonstatic members(再次强调:包含成员函数和数据成员)。
2、它不能被声明为const、volatile或virtual。
3、它不需要经由class object才被调用——虽然大部分时候它是这样被调用的!


五、总结

C++支持三种类型的成员函数:static、nonstatic和virtual,虽然每一种类型被调用的方式都不相同,但也有些联系。

1、static没有this指针;
2、nonstatic有this指针;除了函数参数安插一个额外参数——this指针,编译器都把它和static成员转换为nonmember。
3、virtual member function和虚表关联,类中只要含有虚函数,那么编译器对函数调用的转换其实是转换为使用虚表vptr的形式,请记住,何时才会出现多态,不出现多态时调用虚函数的转换是否也使用虚表的形式?

参考文献:《深度探索C++对象模型》侯捷 译

Delphi是一种面向对象的编程语言,同时也是一款功能强大的集成开发环境。通过使用Delphi,我们可以轻松地开发各种应用程序,包括图像处理应用程序。Tesseract是一种开源OCR(光学字符识别)引擎,它可以将扫描的图像中的文本识别出来,并以可编辑的文本形式呈现出来。通过将Delphi与Tesseract集成使用,则可以实现在Delphi应用程序中进行图像文字识别的功能。 在Delphi中调用Tesseract 4.1,需要进行以下步骤: 1.下载安装Tesseract 4.1,在https://github.com/tesseract-ocr/tesseract/releases 中下载最新版的Tesseract OCR引擎,将其安装至本地计算机中,并将其路径添加至环境变量中。 2.创建一个新的Delphi项目,为了使用Tesseract OCR引擎,需要在Delphi中添加一个组件,如:Tesseract OCR ActiveX组件,可在Google中搜索下载相应的组件。 3.将所需的图像加载至Delphi项目中,这些图像可以来自于扫描结果、拍摄结果等。 4.添加代码:导入Tesseract OCR ActiveX组件,在代码编辑器中通过Tesseract OCR ActiveX组件调用Tesseract 4.1。 5.定义输出格式:在代码编辑器中定义所期望的输出格式,常见格式包括TXT、JSON等。 通过以上步骤,在Delphi应用程序中即可使用Tesseract 4.1进行图像文字识别,这样便可以方便地将扫描文本中的结构化信息转换为可编辑的文本格式,从而更加便捷地进行后续操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值