第四章 Function语意学

本章以一个例子引入:

// Point3d is a class
Point3d obj;
Point3d *ptr = &obj;

// Call class function, thinking what will happen beyond the code
obj.normalize();
ptr->normalize();

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

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

    return normal;
}

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

这章的内容就是为了探究类对象和类对象指针的类成员函数调用机制。

一切开始之前,先来回顾下静态成员函数虚函数的知识点。

静态成员函数是由static关键字修饰的成员函数,它只能访问类的静态成员(静态成员变量和静态成员函数)。

虚函数是由virtual关键字修饰的函数,其主要作用是实现了多态

C++语言的设计准则中要求:类的非静态成员函数和普通的非类成员函数要有相同的效率。即

float magnitude3d(const Point3d *_this) { ... }
float Point3d::magnitude3d() const { ... }

注:成员函数函数名后有const修饰表示该方法不会修改类中的数据成员;若是不小心修改了或者调用了其它非const函数,编译器会报错。

这样似乎有些难理解,非成员函数的方式通过传入类对象指针作为参数,间接引用类的数据成员进行操作,看上去比较没有效率;而成员函数直接可以访问类的成员,应该效率更高。

假设float magnitude3d(const Point3d *-this)的实现如下:

float magnitude3d(const Point3d *_this) {
    return sqrt(_this->_x * _this->_x
               + _this->_y * _this->_y
               + _this->_z * _this->_z);
}

实际上,我们来观察一下编译器将成员函数“转化”成非成员函数的步骤:

1、改写函数原型。编译器会安插一个额外的参数this到成员函数中,使得类对象可以调用该函数:

Point3d Point3d::magnitude(Point3d * const this)

若成员函数被const修饰,则变成:

Point3d Point3d::magnitude(const Point3d * const this)

2、将对非静态成员变量的存取操作转换为由this指针来存取:

{
    return sqrt(
        this->_x * this->_x +
        this->_y * this->_y +
        this->_z * this->_z);
}

3、将成员函数重写成一个外部函数的形式,此过程同时对函数名称进行mangling处理,使它在程序中称为独一无二的语汇:

extern magnitude_7Point3dFv (register Point3d * const this);

转换过程完成。设计到它的每个调用过程也必须转换,于是

ojb.magnitude();

变成了:

magnitude_7Point3dFv(&obj);

ptr->magnitude();

变成了:

magnitude_7Point3dFv(ptr);