zoukankan      html  css  js  c++  java
  • 《深度探索C++对象模型》读书笔记(一)

    前言

    今年中下旬就要找工作了,我计划从现在就开始准备一些面试中会问到的基础知识,包括C++、操作系统、计算机网络、算法和数据结构等。C++就先从这本《深度探索C++对象模型》开始。不同于《Effective C++》,这本书主要着眼于C++实现的底层机制,因此我在写这个系列时默认读者已经熟悉C++的基本语法(包括类、继承、多态、泛型等等),将更多地介绍C++具体是如何实现这些语法的。这次我就先写第一、二章,之后每读两章都会更新该系列。如果你有什么问题,欢迎在博客的评论版块和我探讨,或者联系我的邮箱:kevinstigma@gmail.com,很乐意与你互相切磋、提高。

    第一章:关于对象

     在传统的C程序中,采用的是过程式的思维:“数据”和“处理数据的操作(函数)”是分开来声明的,它们二者之间并没有关联性。但到了C++里,倾向于采用独立的“抽象数据类型”(ADT)来实现数据的封装。从代码结构上来看,C++似乎比C要消耗更高的时间和空间成本,但事实未必如此。相比于C,C++在布局和时间上主要的额外负担是由virtual引起的,这分为两部分:

    1.virtual function机制 用以支持一个有效率的“执行期绑定”。

    2.virtual base class 用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实例”。

    接下来,书里介绍了三种的C++对象模式:简单对象模型、表格驱动对象模型、C++对象模型,其中第三种最为编译器所常用,因此在这里主要介绍第三种。

    C++对象模型中,非nonstatic data members被配置于每一个class object之内,static data members、function members则被存放在class object之外。编译器为每一个class产生一个表格,表格里是一堆指向virtual functions的指针,该表格我们称之为virtual table(vtbl);每一个class object被安插一个指针,指向相关的virtual table,这个指针被称为vptr。vptr的设定和重置由constructor、destructor以及copy assignment运算符自动完成。每一个class所关联的type_info object也存放在virtual table之中。

    该模型的优点是,它的空间和时间的效率较高;缺点在于,如果应用程序本身的代码未改变,而所用到的class object的nonstatic data members有所修改,那么应用程序的代码同样需要重新编译(因为内存分布发生了改变)。

     

    在接下来的篇幅里,书中探讨了struct和class的区别,对于这个问题可以参考这篇博文:http://blog.csdn.net/omegayy/article/details/7470316。其实struct和class从语法本质上差别并不大,无非是二者的默认继承和默认成员访问级别不同。但从一般来说,我们习惯用struct来代表一些简单数据的集合,用class来代表更为复杂的封装、继承的数据。

    与C不同,在C++中,对象的内存分布未必和类中成员的声明顺序一致,比如C++的编译器可能会将protected data members放在private data members前面存储,与声明顺序无关;同理,base classes和derived classes的data members的布局也未有谁先谁后的强制规定。但类中处于同一个access section中的数据,其内存分布顺序是按照声明顺序排列的,只是不同access section之间未必按声明顺序排列。 

     之后书中花了一些篇幅介绍多态的机制,我觉得熟悉C++的人应当对此并不难理解,在这里不再赘述。

    第二章:构造函数语义学

    本章主要介绍了C++中构造函数的生成机制。

    首先是default constructor,C++的编译器真的会为每一个没有声明构造函数的类生成default constructor吗?其实并不是的。书中指出,只有四种情况,会造成“编译器必须为未声明constructor的classes合成一个default constructor”:

    1.member constructor有defualt constructor;

    2.base class具有default constructor;

    3.该类具有virtual funciton;

    4.该类具有一个virtual base class。

    在合成的defualt constructor中,只有base class subobjcets和member class objects会被初始化,而所有其他的nonstatic data member(int、int*、char等类型)都不会被初始化。

    之后是copy constructor。与defualt constructor一样,编译器并不是为所有无用户定义copy constructor的类都创建copy constructor,如上的四种情况下,编译器才会为class合成copy constructor;否则编译器会执行bitwise copy。

    接下来书中介绍了NRV优化以及member initialization list。

    对于NRV(Named Return Value)优化,我们可以看一个例子:

    假设有一个foo函数:

    X foo() 
    { 
        X xx; 
        if(...) 
            return xx; 
        else 
            return xx; 
    }
    

    经过编译器的优化后,代码改成如下样子:

    void  foo(X &result)
    {
        result.X::X();
        if(...)
        {
            return;
        }
        else
        {
            return;
        }
    }
    

    这样就省去了临时对象的默认构造函数、拷贝构造函数、析构函数的成本,从而有助于减少运行时间。另外,有一点要注意的是,NRV优化,有可能带来程序员并不想要的结果,比如当你的类依赖于构造函数或者拷贝构造函数的调用次数时。

    关于member initialization list,我们首先需要知道需要它的时机:

    1.当初始化一个reference member时;

    2.当初始化一个const member时;

    3.当调用一个base class的constructor,而它拥有一组参数时;

    4.当调用一个member class的constructor,而它拥有一组参数时。

    接下来需要注意的一点就是:list中的初始化顺序是由class中member的声明顺序决定的,而不是由initialization list中的排列顺序决定的!这是一个非常容易出错的地方。

  • 相关阅读:
    课堂作业04 2017.10.27
    课程作业 03 动手动脑 2017.10.20
    课程作业 03 2017.10.20
    HDU 3974 Assign the task
    POJ 2155 Matrix
    POJ 2481 Cows
    HDU 3038 How Many Answers Are Wrong
    CS Academy Array Removal
    POJ_1330 Nearest Common Ancestors LCA
    CF Round 427 D. Palindromic characteristics
  • 原文地址:https://www.cnblogs.com/wickedpriest/p/6527181.html
Copyright © 2011-2022 走看看