C++ 支持3种类型member function:static,nonstatic,virtual下面逐一介绍。
Nonstatic Member Functions(非静态成员函数)
C++的设计准则之一: 就是nonstatic member function至少必须和一般的nonmember function有相同的效率。意思是:
void test(const Foo&); // nonmember function
void Foo::test() const; // nonstatic member function
选择下一种方式不会增加任何额外开销,这是因为编译器内部已将"member 函数实例"转换为对等的"nonmember函数实例"。
比如
// 代码没什么实际意义,只作演示
void test(const Foo &it) // 非成员函数
{
return sqrt(this->x * this->x + this->y * this->y + this->z * this->z);
}
void Foo::test() const // 成员函数
{
return sqrt(x * x + y * y + z * z);
}
而Foo::test() const会被改造:
void Foo::test(const Foo *const this)
{
return sqrt(this->x * this->x + this->y * this->y + this->z * this->z);
}
然后再被改造为一个外部函数和name mangling过程。
extern Foo_3testF(const Foo *const this);
则原来的每一个调用操作都会被变为:
obj.test() 变为: test(&obj);
ptr->test() 变为 : test(ptr);
static Member Functions(静态成员函数)
Static member function主要特性是它不属于某一个对象,也就是说它没有this指针。也因此,它会有下面特性:
- 它不能直接存取其class中的nonstatic members。
- 它不能被声明为const,volatile,或virtual。
- 它不需要经由class object对象调用。
如果取用一个static member function的地址,获得的将是其在内存中的位置,也是其地址,由于static member function没有this指针,所有地址类型并不是一个"指向class member function的指针",而是一个"nonmember函数指针",也就是说:
int Foo::static_test() // static member function
&Foo::static_test();
会得到:
int (*)();
而不是:
int (Foo::*)();
Virtual Member Functions(虚拟成员函数)
为了支持virtual function机制, 必须要能够对于多态对象有某种形式的"执行期类型判断法" 比如这种调用:ptr->z(),需要ptr在执行期的相关信息。
最直截了当的方法就是:把必要的信息加在ptr上
- 它所持有的到底是真实对象类型。
- z()实例的位置。
如果这些信息不能够和指针放在一起,那么下一个考虑的就是放在对象本身。但是哪些对象需要这些信息?
struct(class) Foo{
int num;
string str;
};
显然这样的对象是不需要这些信息的。那么很容易想到包含了有virtual函数的需要多态信息。
在实现上,我们可以在每一个多态的class object身上增加2个members:
- 1个字符串或数字,表示class的类型。
- 1个指针,指向某表格,表格中持有virtual function的执行期地址。
然而这些由编译器准备好。执行期要做的,只是在特定的virtual table slot中激活virtual fucntion。
比如:
ptr->z();
那编译器如何设定virtual fucntion的调用呢?
- 我不知道ptr所持有的真正类型,但我知道经由ptr可以存取到该对象的virtual table。
- 虽然我不知道哪一个z()的函数实例会被调用,但我知道z的函数地址存放在virtual table的slot3中(虚函数存放位置有规律)
那么这些信息可以使得编译器将该调用转化为(*ptr->vtpr[4])(ptr);唯一在执行期知道的信息是: slot4所指的到底是哪一个z函数实例。
以上这些推论会经过下面各种代码实践。