object model-Function 语意学
来源:互联网 发布:java培训课 编辑:程序博客网 时间:2024/05/16 16:15
0.member的各种调用方式
注意本节是介绍class member data的各种访问(调用)方式
a.nonstatic member functions(非静态成员函数)
好吧,个人觉得作者自己是非常懂的,但是他讲出来的时候,感觉真的好混乱。通常都是给一个例子,自己体悟,做总结的那些话,总是不放在最显眼的位置。让人抓不到重点。歪果仁都是这么随性吗,讲到哪儿是哪儿
辨析member function 和nonmember function
class People{private:int step;public:People():step(0){} void getUp(){step++;}void work(){step++;}void goSleep(){step++;}//member fuctionvoid PeopleAction(){getUp();work();goSleep();}};//同等功能的nonmember fuction void PeopleAction(const People *p){p->getUp();p->work();p->goSleep();}c++的设计准则之一就是:nonstatic member function 要与一般的nonmember function有相同的效率。
举个小栗子(nostatic member function 其实都是最后被编译器转化成nonmember function的)
nostatic member function
class Point3d{private:double _x;double _y;double _z;public:double magnitude()const{return sqrt(_x*_x+_y*_y+_z*_z);} Point3d normalize()const{double mag=magnitude();Point3d normal;normal._x=_x/mag;normal._y=_y/mag;normal._z=_z/mag;return normal;}};
转化步骤:
1. 改写函数的signature以安插一个额外参数(this指针)到member function中
//第一个const表示函数是const的,第二个const表示this指针是const的 double magnitude( const Point3d * const this)
2.将每一个对nonstatic data member的存取操作改为由this指针存取
{return sqrt(this->_x*this->_x+this->_y*this->_y+this->_z*this->_z);}
3.将member function重新写成一个外部函数,并将函数名称经过magling(所谓的Name Mangling是指在member的名称上加上class名称以及member function的signature)处理,使它成为在程序中独一无二的语汇extern double magnitude_7Point3dFv(const Point3d *const this);
至此,如下调用均会被编译器转化为obj. magnitude ();//变为 magnitude_7Point3dFv(&obj);ptr-> magnitude();//变为 magnitude_7Point3dFv(ptr);
同样的,normalize也会被编译器转化成这样://假设编译器存在NRV(named return value) void normalize_7Point3dFv(const Point3d *const this, Point3d &_result){double mag=this->magnitude();_result._x=this->_x/mag;_result._y=this->_y/mag;_result._z=this->_z/mag;return;}//然后又被写成externextern void normalize_7Point3dFv(const Point3d *const this, Point3d &_result);
b.virtual member function(虚拟成员函数)
同样是从以下两个情况来分析
obj. magnitude ();ptr-> magnitude();对于 ptr -> magnitude( ); 而言,其内部转换为( * ptr -> vptr[ 1 ] )( ptr );,(理由是:指针支持多态,该指针指向的到底是谁的函数还不明确,所以一定得查虚表)其中:Vptr表示右边一起产生的指针,指向virtual table;1是virtual table slot的索引值,关联到normalize();第二个ptr表示this指针。
对于obj.magnitude( ); 而言,由于只有指针和引用才能支持多态,一般的object并不支持多态,所以调用的magnitude()只能是Point3d 的函数。“经由一个class object 调用一个 virtual function”,这种操作总是会被编译器像对待一般nonstatic member function 一样的加以 resolved:
magnitude_7Point3dFv(&obj);
c.static member function(静态成员函数)
如果Point3d::normalize()是一个static memberfunction,以下两个调用:
obj.normalize();ptr->normalize();将被转换为一般的nonmember函数调用,像这样:normalize_7Point3dSFv();//SFv表示static member function,拥有一个空白(void)的参数链表(argument list)normalize_7Point3dSFv();独立于class object之外的存取class object的static data member操作主要有两种方法1.未引入static member function概念之前,程序上的解决之道是将0强制转换为一个class指针,因而提供一个this指针实例
#include<iostream>using namespace std;class Point3d{private:static int const x=5;public:int object_count(){cout<<x<<endl;return x;}};int main(){((Point3d *)0)->object_count();//5return 0;}2.再就是引入了static member function的概念,用static member function 去访问static member data。static member function的主要特性就是它没有this指针,所以1.他不能直接存取nonstatic member data;2.它不需要经由class object才能被调动;3.他不能被声明为const、volatile或virtual(这一条中const和volatile不明白)。
再就是由于static member function由于不存在this指针,所以它的地址类型不是一个“ class member function pointer”而是一个“nonmember function pointer”。(this指针是指向隐含的object的,object中仅仅存储了nonstatic member data 和指向虚函数表的vptr。所以访问了nonstatic member data的nonstatic member function需要this指针,访问虚函数表的virtual member function也需要this指针)
1.virtual member functions(虚拟成员函数)
Virtual function的一般实现模型(无继承):每一个class有一个virtual table,内含class之中有作用的virtual function的地址,然后每个object有一个vptr,指向virtual table的存在。这一节会讨论单一继承,多重继承和虚拟继承这三种情况。
这是我听到的最好的多态解释:在c++中,多态表示 “ 以一个public base class的指针(或引用),寻址找出一个derived class object” 的意思。父类型指针接受子类型对象
a.单继承下的virtual function
对于单继承的derived class而言,它只有一张virtual table
z() 是虚函数,对于ptr->z();而言,到底调用的是谁的z()?对于这个问题,需要清楚两点1.ptr实际上指的是父类型对象,还是子类型对象?2.在知道是哪个类型的对象的情况下,如何找到正确的函数地址。
对于第一个问题:在(具有虚函数的或者父类有虚函数的)class object(因为没有虚函数,也就玩不了多态,这样的class object无需打上标记)上添加一个member(或许是字符串或者一串数字),用以记录指针所指对象的真实类型。
对于第二个问题:在(具有虚函数的或者父类有虚函数的)class object上添加一个指向虚函数表的vptr指针,用以获取虚函数的地址。当然每个子class的虚函数表是需要继承父虚函数表(就是拷贝,可能还会改写或扩充)。
以下是单一继承的情况:
对于derived class而言
现在如果有这样的式子:ptr->z();。一般而言,每次调用z()时,并不知道ptr所指对象的真正类型(这是指编译器并不知道,而不是程序员并不知道),但是可以知道经由ptr可以存取到该对象的virtual table,可以知道每一个z()函数地址被放在slot 4中。所以编译器可以将该调用转化为:(*ptr->vptr[ 4 ] )( ptr );,在这一转换中,唯一在执行期才能知道的东西是:slot 4所指的到底是哪一个z()函数实例。1.它可以继承base class 所声明的virtual function,具体地说,父类的virtual function的实体地址会被拷贝到子类的virtual table中去。
2.它可以拥有自己的virtual function,这表示它自己的virtual function实体地址必须拷贝到自己的virtual table中去。
3.它可以改写父类的virtual function,这表示,该改写后的virtual function的实体地址需要替换原来父类虚函数实体地址在自己的virtual table的位置。
b.多重继承下的virtual function
对于多重继承下的derived class而言,它有n-1张virtual table(n为derived class上subobject的个数)
在多重继承中支持virtual function,其复杂度围绕在第二以及后继的base class上 2. “ 必须在执行期调整this指针 ”。
小栗子:
#include<iostream> using namespace std; class Base1 { protected: float data_base1;public:Base1(){}virtual ~Base1();virtual void speakClearly();virtual Base1 *clone()const; }; class Base2 { protected: float data_base2;public:Base2(){}virtual ~Base2();virtual void mumble();virtual Base2 *clone()const; }; class Derived:public Base1,public Base2 { protected: float data_derived;public:Derived(){}virtual ~Derived();virtual Derived *clone()const; };对于第二个问题 “ 必须在执行期调整this指针 ”的引出和解决:
Base2 *pbase2=new Derived;新的derived 对象的地址必须要调整,指向Base2 subobject(因为new Derived的地址是指向最左端的Base1 subobject的,而Base1与Base2并无继承关系,且两者均存在clone(),但是又不相同)。以下是编译时期的调整Derived *temp=new Derived;//创建新对象 Base2 *pbase2=temp?temp+sizeof(Base1):0;//Base1在Base2前,故Base2的subobject在Base1的subobject之后,此句将对象指针指向Base2的subobject当要删除对象时,又必须将对象地址指回起始处,以求删除整个对象。
delete pbase2;然而上述的offset加法却不能够在编译时期直接设定,因为pbase2所指的真正对象只有在执行期才能确定。所以必须在执行期调整this指针。为解决这个问题,一帮子人想了一种办法Thunk技术
所谓thunk是一小段assembly代码,用来(1) 以适当的offset值调整this指针(2)跳到virtual function去。例如,经由一个Base2执行调用Derived destructor,其相关的thunk可能看起来是这个样子:
Pbase2_dtor_thunk:This+=sizeof(base1);Derived::~Derived(this);Thunk技术允许virtual table slot继续内含一个简单的指针,因此多重继承不需要任何空间上的额外负担。Slots中的地址可以直接指向virtual function,也可以指向一个相关的thunk(如果需要调整this指针的话)。于是,对于那些不需要调整this指针的virtual function而言,也就不需承载效率上的额外负担。
然后就有了下图未打星号的依旧执行,打了星号的执行thunk(用以调整this指针,并执行虚函数)。
在多重继承下一个derived class内含n-1个额外的virtual table。一如章一《关于对象》中的描述,derived class 与最左端基类共享一张virtual table作为主要表格,其他基类各有一张virtual table作为次要表格。
Base::mumble()打星号是因为this指针必须指向Base2 subobject才能正确调用Base2::mumble方法,而derived::~derived()和derived::clone()打星号是因为,拷贝derived class object和删除derived class object的操作对象必须是整个的derived class object,所以必须调整this指针回到起始处。
c.虚继承下的virtual functions
在虚基类中最好不要声明nonstatic member data。
2.指向member function的指针
取一个非静态成员函数nonstatic member function的地址,如果该函数是nonvirtual,则得到的结果是它在内存中真正的地址。它也需要被绑定于某个class object的地址上,才能够通过它调用该函数。所有的nonstatic member functions都需要对象的地址(以参数this指出)。
使用一个"member function"指针,如果并不用于virtual function, 多重继承,virtual base class等情况的话,并不会比使用一个"nonmember function指针”的成本更高。而virtual function 的出现,会使得"member function指针“更复杂化。
b.指向Virtual Member Functions的指针
float (Point::*pmf)() = &Point::z;Point *ptr = new Point3d;pmf, 一个指向member function的指针,被设置为Point::z()(一个virtual function)的地址。ptr->z(), 被调用的是Point3d::z()。我们从pmf间接调用z()呢?(ptr->pmf)(); 调用的是Point3d.z()吗?答案是yes。问题时如何实现的呢?
3.Inline Functions我们知道,对一个非静态数据成员取其地址,将获得该函数在内存中的地址,然而面对一个虚拟函数,其地址在编译时候是未知的,所能知道的仅是virtual function在其相关的virtual table中的索引值。也就是说,对一个virtual member function取其地址,所能获得的只是一个索引值。例如:
class Point{ public: virtual ~Point(); float x(); float y(); virtual float z();};&Point::~Point = 1; 取&Point::x()的地址则是内存中的地址。通过pmf来调用z(),会被转化为一个编译时期的式子,一般形式如下所示:(*ptr->vptr[ (int)pmf ] ) (ptr);
一般而言,处理一个inline函数,有两个阶段:
一:分析函数定义。如果函数因其复杂度,或因其建构问题,被判断不可成为inline,它会被转为一个static函数。
二:真正的inline函数扩展操作是在调用的那一个点上。这会带来参数的求值操作(evaluation)以及临时性对象的管理。
在inline函数的扩展进行处理时,大部分的编译器厂商似乎认为不值得在inline支持技术上做详细的讨论。我们只有进入到汇编代码中,才可以看到是否真正的实现了Inline。一般而言,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段内,拥有一个独一无二的名字。Inline函数中的局部变量(展开处同名),再加上有副作用的参数,可能会导致大量临时性对象的产生。特别是如果它以单一表达式被扩展多次的话。如果一个Inline函数被调用多次的话,会产生大量的扩张码,使程序的大小暴涨。所以,我们需要小心的处理。
- object model-Function 语意学
- object model-构造函数语意学
- object model-构造、解构、拷贝语意学
- Inside The C++ Object Model(二)构造函数语意学
- Inside The C++ Object Model(三)Data语意学
- Inside The C++ Object Model(四)函数语意学
- Inside The C++ Object Model(六)执行期语意学
- C++ Function语意学
- Function 语意学
- Function语意学
- 第四章:function 语意学
- 第四章 Function 语意学
- Inside the C++ Object Model 学习笔记 第五章构造 解构 拷贝语意学
- Inside the C++ Object Model 学习笔记 第四章 函数语意学 测试代码 乱写的
- Inside the C++ Object Model 学习笔记 第四章 函数语意学
- Inside The C++ Object Model(五)构造、拷贝、析构语意学
- [读书笔记] Inside C++ Object笔记(第4章: Function语意学)
- function语意学和析构函数语意学
- 国内无人机飞行控制器的研究
- iPhone 6S 有苹果不愿意讨论的问题
- android中接口回调机制
- Java 文件过滤 FileFilter
- QT上使用OpenCV
- object model-Function 语意学
- 黑马程序员-GUI图形用户界面
- 你用过这种奇葩的C#注释吗
- POJ 2488 *** A Knight's Journey
- iOS 使用NJKWebViewProgress做webview进度条
- 枚举法-填数
- 【BeiJing2011】【BZOJ2458】最小三角形
- 黑马程序员 JavaSE-01 Java概述
- shell配置--《shell脚本编程诀窍》