第四章函数语义学
众所周知的,在C++
中,跟类相关的成员函数有三种,静态函数、非静态函数、虚拟函数。本章介绍了这几种函数的来源、用在何处、编译器如何实现、及效率。
非静态成员函数:
简单的说,非静态成员函数就是非成员函数中加入了一个this
指针的参数(不需要其他更多的解释了)。
书中还特意提到了名称的特殊处理(Name Mangling
)。
一般来说,对于一个非静态成员函数,Name Mangling
的处理后的函数名是:函数名+类名+参数名。加类名是为了区分多重继承的相同名字的成员函数;而加参数名,则是区分函数重载。作者还特意提到返回值并不加入name mangling
。
如果仅仅是返回值不同的两个成员函数,VC
编译器会报错:
error
C2556: 'float __thiscall namemangling::x(void)' : overloaded function
differs only by return type from 'void __thiscall namemangling::x(void)'
静态成员函数:
这里没有什么内容。作者着重介绍了静态成员函数的来源。静态成员函数的主要特征在于:没有this
指针,由这个主要特征,衍生出以下主要特征:
1、
不能直接存取类中的非静态成员;
2、
不能声明为const
、volatile
、virtrual
;
3、
不需要经由类对象调用;(抄书)
虚拟成员函数:
说到虚拟成员函数,就必须谈到多态。虚拟函数的产生就是为了满足多态的需要(我是这么理解的)。要把这个道理说清楚还真不容易,作者花了整整六页,画了很多图表,举了很多例子试图将虚拟继承将清楚。这里我也尝试一下,以更短的语言将最简单虚拟继承和多态将清楚。
先举个最简单的例子:
有一个基类,
Class Point
{
Public:
Virtual ~Point();
Virtual Point& mult() = 0;
Protected:
Float _x
};
中,跟类相关的成员函数有三种,静态函数、非静态函数、虚拟函数。本章介绍了这几种函数的来源、用在何处、编译器如何实现、及效率。
非静态成员函数:
简单的说,非静态成员函数就是非成员函数中加入了一个this
指针的参数(不需要其他更多的解释了)。
书中还特意提到了名称的特殊处理(Name Mangling
)。
一般来说,对于一个非静态成员函数,Name Mangling
的处理后的函数名是:函数名+类名+参数名。加类名是为了区分多重继承的相同名字的成员函数;而加参数名,则是区分函数重载。作者还特意提到返回值并不加入name mangling
。
如果仅仅是返回值不同的两个成员函数,VC
编译器会报错:
error
C2556: 'float __thiscall namemangling::x(void)' : overloaded function
differs only by return type from 'void __thiscall namemangling::x(void)'
静态成员函数:
这里没有什么内容。作者着重介绍了静态成员函数的来源。静态成员函数的主要特征在于:没有this
指针,由这个主要特征,衍生出以下主要特征:
1、
不能直接存取类中的非静态成员;
2、
不能声明为const
、volatile
、virtrual
;
3、
不需要经由类对象调用;(抄书)
虚拟成员函数:
说到虚拟成员函数,就必须谈到多态。虚拟函数的产生就是为了满足多态的需要(我是这么理解的)。要把这个道理说清楚还真不容易,作者花了整整六页,画了很多图表,举了很多例子试图将虚拟继承将清楚。这里我也尝试一下,以更短的语言将最简单虚拟继承和多态将清楚。
先举个最简单的例子:
有一个基类,
Class Point
{
Public:
Virtual ~Point();
Virtual Point& mult() = 0;
Protected:
Float _x
};
Class Point2D : public Point
{
Public:
~Point();
Point2D& mult();
};
对于这个最简单的虚拟继承链,假设有一个表达式为:
Ptr->Point();
那么,这个函数到底是指向哪个类的函数呢?C++
又是如何实现的?本段就是解决这个问题。
在C++
中,对于每一个存在虚函数的类中,都会添加一个指针vptr
。该vptr
指向的是该类的虚函数表。类的虚函数表中包含了类中所有的虚函数(
即使有纯虚函数,也被包含在内,虽然基本上是不可能有调用的)
。如果该类中没有虚函数,但是所继承的基类中有同名虚函数,那么该虚函数也会被放到类的虚函数表中。也就是说对于类Point
和Point2D
的对象,都有一个自己的虚函数表。并且类中有vptr
指向这个表。
所以对于Ptr->Point()
,编译器先通过Ptr
找到该类对象的vptr
,根据函数名字又可以得到函数在虚函数表中的索引,进而找到该函数的执行代码。如果Point()
在虚函数表中的索引为2
,则这个表达式可以写为:
(*Ptr->vptr[2])(Ptr);
这样,只需要在执行期知道Ptr的类型,就可以调用合适的函数处理了。