码迷,mamicode.com
首页 > 其他好文 > 详细

The Semantics of Function

时间:2015-05-10 15:48:40      阅读:115      评论:0      收藏:0      [点我收藏+]

标签:c++   class   object   摩讯   

本次博文的主要议题是成员函数,涉及到的知识大部分出自于深度探索C++对象模型 一书,建议感有志之士自行学习,因为我开始有点讨厌别人喂饭给我吃。

今天,走在去往图书馆的路上突然萌生出一个想法,一个关于如何读书学习的想法。当时我在思考阅读Inside the Object Model 一书,书中很多知识我看的时候是知道的,但是合起书本,很多东西却又是显的陌生。其次就是这样一个问题,有的内容我第一次真的是看的云里雾里,但是第二次,第三次真的就有所得,莫非这个是对个人坚持异或是勤奋的奖励。所以我萌生了好书看三遍的想法,并且今天我及时的记录下来,希望自己以后也能记住今天写下的话。

前几日刷quora 的时候看了一句话,分享给大家,同时自勉。Konwledge is tha you koww tomato is fruit;Wisdow is that you never put tomato in salad 希望自己没有记错。
废话到此为止。


The Semantics of Function

标题同上一篇文章很像。上一篇写的是数据成员,今天总结下类中的函数成员。
引例:

//assume we already have a class.
origin.normalize();
auto ptr=&origin;
ptr->normalize();

试问一下,上述的两种调用有区别嘛?废话,肯定有区别啦,区别在于我们调用的函数究竟是什么类型?
在类中我们会遇到如下几种函数:member function ,static member function,virtual function ,friend function经常写代码的人肯定都遇到过了。下面我们会分析调用方式之间的区别.

4.1 The way of call member function

最古老的C++ 刚开始只支持nonstatic member functionvirtual functionstatic function .
1.nonstatic member function 的调用
principle:C++的涉及准则之一就是非静态成员函数至少要和一般的非成员函数有相同的效率。也就是说:

friend int magnitude3d(const Point3d* _this);
//by the way ,the const before this line mean?
int Point3d::magnitude3d()const;

要有着同样的效率。在我们的第一个函数中,我们肯定是通过_this 指针来存取,比如_this->x 存取x .乍看之下没效率,但是我们的成员函数也是如此做的。我们的成员函数会被转换成非成员函数。将经历如下步骤:
1.修改函数的原型,安插一个this指针。如果我们的成员函数是非const的,那么会转换成:
int Point3d::magnitude(Point3d* const this);
如果成员函数是const的,那么会转换成:
int Point3d::magnitude(const Point3d* const this); 记住一点,const成员函数至少用了修饰this指向的内容,而不是修饰this自身。
题外话:

const int * ptr;//ptr是可变的,但是指向的内容是不可变的
int const * ptr;//同上
int * const ptr;//ptr自身不可变,但是指向的内容可变
const int * const ptr;//自身和指向的内容都不可变

2.将对nonstatic data member 的存取变为经由this指针存取。不赘述。
3.将member function 重写写成一个独一无二的外部函数。独一无二过程叫做mangling处理。如:extern mangnitude_7Point3dFv(register Point3d* this);
那么调用操作会变为:magnitude_7Point3dFv(ptr);
如果我们的函数的返回类型是Point3d 那么函数可能会被实施NRV优化,也称具名返回值优化策略。如:

//signature
Point3d Point3d::func();
//we must return a Point3d object when we call the function
//so ,we ues NRV.
void func_7Point3dFv(Point3d* const this,Point3d& _result);
//inset two args into the parameters.wo may often see this in modern C++ .

现在回头看一下func_7Point3dFv这个神奇的名字是如何产生的。
一般而言,member的名称前会被加上class的名称,形成独一无二命名。如:

class test{public: int val;};
//其中的val可能会转换为val_test;

编译器如此做的原因可能是因为在继承体系中,派生类声明了同名变量,但是经过这样处理就很好的区分开了。
上面是针对数据成员,它们无法进行重载,但是成员函数可以重载,那么仅仅加上类名是不足以解决问题的,所以我们会加上他们的参数链表(参数可以从函数原型中获得)于是我们可以看到如下例子:

class test{
public:
    int val;
    void func_7testFv();
    float func_7testFi(int num);
   //还知道区分重载的依据是什么嘛?
//F表达function,v表示void,i表示int.
}

上面的例子仅仅是某些编译器的做法,业界暂未统一。目前的编译系统中还有一种所谓的demangling工具,实施逆向操作。
2.Virtual Member Functions
秉承上文的函数,如果我们的调用的函数是虚函数的话那么转换操作会变成:(*ptr->vptr[1])(ptr); 其中涉及到的内容都很简明,如虚表,虚表中的索引。
3.静态成员函数:其调用将会转换为非成员函数的调用,类似一开始的成员函数的转换。
4.在C++ 引入静态成员函数之前,要求所有的成员函数调用都必须经由类的对象来调用,但是只有当需要在内部使用this指针时,我们才需要绑定一个对象。因此有的情况是不需要通过对象来调用成员函数的。后来引入了静态成员函数,它的主要特性就是没用this指针,次要特性根源于主要特性。比如:1.不能直接存取非静态数据成员;2.不能够被声明为const,volatile,virtual;3.不需要经由object调用。
对于一个静态成员函数去地址得到是其在内存中的真实地址,由于其无this指针,所以指针的类型不是指向成员函数的类型,而是一个非成员函数指针。如:

&Point3d::func;//func is static function .
得到的是int(*)();而不是int (Point3d::*)();

由于缺乏this指针,近乎等于非成员函数。它提供了一个意料之外的好处,可以成为一个callback 函数,也就是回调函数。

4.2 Virtual Member Functions

1.下面一节内容是对上文的虚拟成员函数的拓展内容,这一节内容稍微有点高深莫测,而且有些方面已经不仅仅是语言自身的问题,涉及到编译器内部的优化以及解决方案,所以很多东西只能是谈资以及思想级别的。其实我们已经很熟悉最基本的虚函数实现模型,我们的编译器合成一个指向虚表的指针,虚表中内含的是函数的地址。本节要走访一组可能的设计,然后以单一继承,多重继承,虚拟继承几个层面来探究下实现模型。
在支持虚函数机制的时候遇到一个问题,那就是我们什么时候需要支持多态,总不能所有的类都内含虚指针(实际根据书上的内容透露,的确考虑过这个想法,但是因为负担的问题搁置了)。现在我们应该都知道了,区分的依据是格局类中是否有Virtual function 而不是通过关键词class or struct 。还有一个问题就是什么样的额外信息需要存储起来,也就是说当我们有如下调用:ptr->z() z是一个虚函数,那么什么信息能让我们在执行期正确调用z的实例?我们需要知道的是ptr 的真实类型以便选取出正确的实例。其次就是z()实例在虚表中的正确位置。记住一点,我们的虚函数在表格是具有固定索引的(在一个继承体系中,如果z()在基类中表格索引是1,那么在派生类中的索引也是1,同我们是否override 无关系。在处理复杂的单一继承链中,我们只要秉持着只负责上一级的原则即可。
在单一继承模型中,虚拟机制的实现很好,也很简单,但是后两者就没那么简单了。
2.多重继承下的虚函数:
多重继承下虚机制复杂在于其第二个及后继的基类以及必须在执行器调整this 指针。因为篇幅过大,而且恐怕自己领悟的不够深,所以留待读者自行阅读理解。
3.虚拟继承下的虚函数:这个地方就更复杂了,而且不常用,所以暂时搁置不谈。、
4.3 函数的效能:
根据上面的简述,我们可以知道,成员函数,静态成员函数,以及友元函数有着大致的效能,毕竟他们在编译器内部的转换形式大致相等。至于虚函数的情况下,单一继承中虚函数好于多重继承的虚函数,好于虚拟继承下虚函数。最后值得一提的是内联函数的存取效率可能会好点,但这个东西你只能通过汇编去查看了。
4.4 指向成员函数的指针:
前面一篇我们提过,取非静态数据成员的地址得到的是其在对象中的布局的位置+1,我们可以认为它是一个不完整的值,需要被绑定在对象上才可以被存取。当我们取非静态成员函数的地址得到的是什么呢?答案是其在内存中真是位置,但是这个也是不完整的,因为我们仍然要通过对象调用它。我们得到的是指向成员函数的指针,而不是普通的函数指针类型。但是当我们存取静态成员函数的地址得到就是普通的函数指针类型,因为它的值是完整的,也就是说我们不需要通过对象来调用它。
但是当我们存取一个虚函数的地址会发生什么?我们得到是其索引值
4.5内联函数:
内联函数相比于宏函数是更好的。关于内联我们要知道,这个只是向编译器发出的一个请求,并不是一定会内联。至于是否内联,我们可以通过汇编来查看。但是不得不承认,内联函数在某些情况下的确是提高了效率,但是可能会让程序变得臃肿。

最后:有些地方省略了,可能以后有点新感悟会继续更新。

The Semantics of Function

标签:c++   class   object   摩讯   

原文地址:http://blog.csdn.net/u014343243/article/details/45620679

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!