深度探索C++对象模型——Function语意学

前言:

最近在读《深度探索C++对象模型》,收获不小,整理一些笔记,一来总结加体悟,二来希望以后在某些知识点的遗忘时能快速拾起也希望对读者有一定的帮助。

C++支持三种类型的成员函数

static
nonstatic
virtual

static成员函数

  • 静态成员函数不能直接存取非静态成员(包括静态成员数据和静态成员函数):因为静态成员函数是属于类的,不属于任何一个对象,在类对象创建以前就已经存在了。而非静态成员属于类,在类对象创建时才生成,如果在一个静态成员函数中直接存取非静态成员相当于在操作一个不存在的东西。
  • 静态成员函数不能是const成员函数:const成员函数其实是指其隐含的this指针指向的对象是const的,因为this指针永远指向当前对象,所以指针本身是const,那const成员函数的隐含参数就是一个指向const对象的const指针。既const成员函数本身是属于创建它的对象其this指针指向的对象只能是常量对象,而static成员函数属于类,它并不关心调用它的对象是谁,没有隐含的this指针,不存在关心指针指向的对象是不是常量的问题。同样的道理,静态成员函数也不支持virtual。
  • 网上看到篇分析静态成员函数的文儿,觉得挺好,贴个链接 http://blog.csdn.net/morewindows/article/details/6721430

Nonstatic Member Functions(非静态成员函数)

注意,以下,如果成员函数或者成员数据未强调静态,则默认为非静态的。
C++的设计准则之一,就是非静态成员函数至少必须和一般的nonmember function(非成员函数)有相同的效率。因为编译器内部会将“成员函数实体”转换成对等的“非成员函数实体”。
假如现在有一个3d图形类:Point3d定义如下:

class Point3d
{
public:
    Point3d()
    {
        x_ = 0;
        y_ = 0; 
        z_ = 0;
    }
    Point3d(float x, float y, float z){
        //
    }
    float Sum()
    {
        return (x_*x_ + y_*y_ + z_*z_);
    }
private:
    float x_;
    float y_;
    float z_;
};

以及,一个非成员函数Sum:

float NonSum(const Point3d* s)
{
    return (s->x_*s->x_ + s->y_*s->y_ + s->z_*s->z_);
}

成员函数float Point3d::Sum经过三个步骤被编译器内化为非成员函数NonSum函数的形式,如下:

  1. 改写成员函数的原型,安插一个额外的参数,即this指针到成员函数中,用以提供一个存取管道,使class object得以调用该函数,变成:
/*non-const nonstatic member*/
float Point3d::Sum(Point3d* const this)

/*如果是常量成员函数则*/
float Point3d::NonSum(const Point3d* const this)

2.对每一个nonstatic data member 的存取操作改为经由this指针来存取

{
    return (this->x_*this->x_ + this->y_*this->y_ + this->z_*this->z_);
}

3.将member function重新写成一个外部函数,对函数进行”mangling”处理,使它成为程序中独一无二的语汇。

如此,这个函数就算是转换好了,每一个调用操作也必须转换,于是

obj.Sun();
//变成:
mangling.._Sum(&obj);

ptr->Sum();
//变成:
mangling.._Sum(ptr);

这里Sum函数的前缀代表转换后的语汇,具体是什么视编译器而定。

名称的特殊处理(Name Mangling)

编译器在实现命名机制时,为了区分两个类的同名成员给每个成员的命名再加上其类型,为了区分两个重载函数,用(函数名+参数类型+参数数目)的方式区分,但是没有规定根据函数返回类型来区分,这就是为什么我们在定义两个重载函数的时候当两个函数只有返回值类型不一样时编译器会报重复定义的错误。注意,这里的函数参数类型,static int和int的类型是一样的都是int,切不可认为它们是不同的类型。

Virtual Member Functions(虚拟成员函数)

注:以下将Virtual Member Functions简称为virtual函数或者虚函数

先简单说一下我对虚函数(virtual函数)的一点理解,在OO(Object Oriented)设计中,通过虚函数来实现多态。具体实现的方法就是,基类定义一个虚函数,各个子类继承基类的虚函数并改写(也可以不改写),在调用操作的时候通过把子类的指针/引用赋给基类指针/引用,然后通过该被赋值的指针调用虚函数,调用的就是赋值符号右边的指针/引用类型对象的虚函数。其中,因为多态性会引入一些额外的执行期信息,所以必须把需要支持多态的类和不需要的类区分开来。而识别一个class是否支持多态的唯一适当的方法是看它是否有任何的virtual函数,只要class拥有一个virtual函数,它就需要这份额外的执行期信息。
我们可以简单概括一下虚函数的实现原理:

  • 每个含有virtual函数的类都有一个虚函数表(虚表),这个虚表里面放着索引,每个索引关联着一个virtual函数地址,编译器按照virtual函数声明顺序将其索引放置在虚表中,可根据该索引找到虚函数。
  • 每个含有(继承或者自身定义)虚函数的类对象都会在构造函数执行的时候创建一个指向该类虚函数表的指针,类对象可根据该指针找到其所属类的虚函数表,虚表里面对应的虚函数们。
  • 子类继承父类时,先将父类虚函数放进虚表,然后用新的函数指针代替子类改写后的虚函数地址,再将新定义的虚函数放在虚表后面。
    可以参考:http://www.cnblogs.com/malecrab/p/5572730.html

好了,紧接我们上面提到的Name Mangling,我们来看一下,编译器是如何对虚函数实行内部转化的:

如果normalize()是一个Virtual Member Function,那么以下的调用
ptr->normalize()
将会被内部转化为:

(*ptr->vptr[1])(ptr);
  • vptr表示编译器产生的指针,即每个对象都有的指向虚表的指针。事实上,vptr名称也会被mangled,因为在一个复杂的class派生体系中,可能存在多个vptr。
  • 1是虚函数表的索引值,关联到normalize()函数
  • 函数参数位置的ptr表示this指针

接着补充一下,如果Point3d::normalize()是一个静态成员函数的情景:
一下两个操作:

obj.normalize();
ptr->normalize();

将会被转换为一般的的nonmember函数调用,这也就是为什么当你定义两个函数像这样:

static void print(){}
void print(){}

会在编译时报重复定义错误的原因。

虽然静态成员函数大部分时候都被class object调用,但是它真的不是必须经过类对象才能调用,它是属于类的!还是上面说的原因,static member functions缺乏this指针所以它差不多等于nonmember函数,如果取其地址,获得的是其在内存中的位置,并不是一个指向class member function 的指针而是一个指向nonmember函数指针。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《深度探索C++对象模型》是由侯捷所著的一本经典的C++图书,该书于2012年由机械工业出版社出版。本书的主要内容涵盖了C++对象模型的深入解析和探讨。 在书中,作者详细讲解了C++中的对象模型和相关的概念,如类、对象、继承、多态等。作者首先介绍了C++对象模型的基本概念和特点,包括对象的内存布局、虚函数表和虚函数指针等。然后,作者深入探讨了C++中的继承机制和多态性,包括单继承、多继承、虚继承等。作者还详细介绍了虚函数的实现原理和使用方法。 在书中,作者对C++对象模型的实现细节进行了深入的剖析,包括成员变量和成员函数的内存布局、函数指针和成员函数指针的用法等。同时,作者还讨论了C++中的一些高级特性,如模板、内存管理和异常处理等。通过对C++对象模型深度探索,读者可以更好地理解C++的内部机制和原理,提高程序设计和开发能力。 《深度探索C++对象模型》适合具有一定的C++编程基础的读者阅读,尤其是对C++对象模型感兴趣的读者。通过阅读本书,读者可以进一步了解C++的底层实现和运行机制,从而提高自己的编程能力和代码质量。此外,本书还提供了大量的示例代码和实践案例,可以帮助读者更好地理解和应用所知识。 总之,《深度探索C++对象模型》是一本深入探讨C++对象模型的经典著作,通过对C++的底层实现和内部机制的剖析,帮助读者深入理解C++编程语言,并提高自己的软件开发能力。 ### 回答2: 《深度探索C++对象模型》是由Stanley B. Lippman于1994年所著的一本经典畅销的C++书籍,该书详细介绍了C++对象模型的内部实现细节。 C++对象模型是指C++编译器在处理对象、继承、多态等面向对象特性时所采用的具体实现方式。这本书通过对对象模型的剖析,帮助读者深入理解C++的内部工作原理,从而写出更高效、更可靠的C++代码。 在《深度探索C++对象模型》中,作者首先介绍了对象、虚函数、继承等C++核心概念,然后详细讲解了C++对象模型的构建过程,包括对象布局、成员函数指针、虚函数表等。作者逐步深入地剖析了C++对象模型在内存中的表示方式,解释了为什么C++可以支持如此强大的面向对象特性。 此外,本书还探讨了一些高级主题,如多重继承、虚拟继承、构造函数和析构函数的执行顺序等。对于想要深入C++的读者来说,这本书提供了一些宝贵的技术手册和实用的经验。 尽管《深度探索C++对象模型》的出版时间是1994年,但它仍然被广泛认可为C++对象模型的经典之作。在2012年时,由于C++的发展和演进,也许一些内容已经有些过时,但很多基本概念和原理仍然适用。 总而言之,《深度探索C++对象模型》是一本值得阅读的C++经典著作,通过深度探索C++对象模型,读者可以更加深入地了解C++的内部工作原理和实现方式,提升自己的开发技能。 ### 回答3: 《深度探索C++对象模型》是一本于2012年出版的书籍。该书的作者Andrews和Sorkin以全面的角度深入探讨了C++对象模型。该书重点介绍了C++中的对象表示、虚函数、继承、多重继承、构造函数、析构函数等内容,以及与之相关的语法、原理和底层实现。 这本书为读者揭示了C++对象模型的奥秘,让人更加深入地理解C++语言中的类和对象。作者通过分析对象布局、虚函数表、虚函数调用、多继承中的数据布局和函数调用等等,解释了C++对象模型的实现机制。 在读者了解C++对象模型的基础上,该书还介绍了如何有效地利用对象模型来提高程序的性能。作者讨论了虚函数的成本以及如何减少虚函数调用的开销,提供了一些优化技巧。此外,书中还对C++的构造函数和析构函数进行了深入的讨论,详细解释了构造函数和析构函数的执行机制和注意事项。 总的来说,《深度探索C++对象模型》是一本深入剖析C++对象模型的重要参考书籍。通过阅读该书,读者可以更加全面地了解C++的类和对象的实现原理,对于理解C++语言的底层机制和优化程序性能具有积极的作用。无论是对于初者还是有一定C++基础的开发人员来说,该书都是一本值得阅读的重要参考书。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值