C++ 对象模型 第四章 函数语意学

1)静态成员函数不能声明为const也不能声明为虚函数,它不能直接访问非static数据成员。


2)名称特殊处理(name mangling),一般而言,member的名称前加上class名称,形成独一无二的命名。但如果你声明(extern “C”)就会抑制nonmember function的mangling效果


3)如果normalize()是一个virtual  member  function,那么以下的调用:
ptr->normalize();
将会被内部转化为:(*ptr->vptr[1])(ptr);
其中,1是virtual table slot的索引值,关联到normalize()函数
第二个ptr表示this指针


4)静态成员函数
Static member  function的主要特性就是它没有this指针
①它不能够直接存取class中的nonstatic members。(没有this指针,如果通过this->member存取)
②它不能声明为const volatile 或是virtual
③它不需要经由class object才能被调用


如果取得一个static member function的地址,获得的将是其在内存中的位置,也就是其地址。由于static member function没有this指针,所以类型不是一个指向class member function的指针,而是一个“nonmember 函数指针”
int (*)();//非成员函数指针
int (point3d::*)();//point3d成员函数指针




5)多态性
C++中,多态性表示“以一个public base  class的指针”,寻址出一个derived class object的意思。
因此识别一个class是否支持多态,唯一合适的方法就是看看它是否有任何virtual  function。


如果z()是一个virtual  function,那么什么信息才能正确调用z()呢?(ptr->z())
我们需要知道①ptr所指对象的真实类型②z()实例的位置,以便我能调用它
该信息使得编译器进行如下转化:
(*ptr->vptr[4])(ptr);
唯一一个在执行期才能知道的东西是:slot 4到底指向哪一个z()实例




6)指向member function的指针
对一个nonstatic data member 取地址,得到的结果是该member在class布局中的bytes位置(加1)。它是一个不完整的值,它需要绑定于某个class object地址上,才能够被存取。
取一个nonstatic member function的地址,如果该函数时nonvirtual,得到的结构式它在内存中真正的地址。然后这个值不是完全的,它需要绑定于某个class object的地址上,才能够通过它调用函数。所有的nonstatic member function都需要对象的地址(以this指出)


一个指向member function指针的声明:
double (point::*pmf)();
//初始化
double (point::*coord)()=&point::x;
//调用
(orgin.*coord)();
or
(ptr->*coord)();


static member funciton的类型是 函数指针,而不是指向member function的指针




7)支持“指向virtual  member functions”的指针
float (Point::*pmf)()=&point::z;
point *ptr=new point3d;
ptr->z();//被调用的是point3d::z()
(ptr->*pmf)();//被调用的仍是point3d::z()




对一个virtual member function取其地址,所能获得的只是一个索引值。




8)inline 函数
内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?如果所有的函数都是内联函数,还用得着“内联”这个关键字吗?内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。


以下情况不宜使用内联:


(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。


(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。


一般而言,处理一个inline函数,有两个阶段:
①分析函数定义,以决定函数“intrinsic inline ability”。如果函数因其复杂度,或因其构建问题,被判断为不可成为inline,它会被转化为一个static函数。
②真正的inline函数扩展操作是在那一个点上。这会带来参数求值以及临时性对象的管理。


9)形式参数
每一个形式参数都会被对应的实际参数取代。一般而言,面对“会带来副作用的实际参数”,通常需要引入临时变量。也就是说,如果实际参数是一个常量表达式,我们可以在替换前完成求值。如果既不是一个常量表达式,也不是个带有副作用的表达式没那么就直接代换之。


在inline函数中加入一个局部变量需要额外的处理。inline扩展开来的时候,为了维护其局部变量,将重顶一个变量(对该局部变量做mangling)。
需要说明的是,inline函数对于封装提供了一种必要的支持,可以有效存取封装于class中的nonpublic数据。它同时也是C程序中大量使用的#define的一个宏全代替品。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值