搜索
您的当前位置:首页正文

C++ Function语意

来源:二三娱乐

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函数实例。

以上这些推论会经过下面各种代码实践。

Top