深度探索C++对象模型

学习《 C++对象模型》 侯捷著

极力推荐Adoo写的 小结:   http://www.roading.org/category/shen-du-tan-suo-cdui-xiang-mo-xing.html



 implicit   暗中的,隐含的
 explicit   明确的
 trivial    没有用的
 non-trivial  有用的
 memberwise   对每个member进行.. 操作
 bitwise      对每个bit进行 ... 操作 ( bitwise copy  位拷贝操作) 
 semantics    语意 

第一章 关于对象

1. C程序员的巧计有时候会成为C++程序员的陷阱。 例如把单一元素的数组,放在一个struct的尾端,于是每个struct objects可以拥有可变大小的数组:

struct mumble{

          int nTypes;

          char pc[1];

};


struct mumble *pMum = (struct mumble *)malloc( sizeof( struct mumble) + strlen(string ) +1);  //string是个很大的字符串,例如“abcdefgheilaieiijf......"


第二章 构造函数语意学

   2.1  default constructor 在被编译器需要的时候,产生出来。什么时候被需要? 

           四种情况下会合成 implicit nontrivial default constructors, :

            a.  带有default constructor 的 member class object

            b. 带有 default constructor 的 Base Class

            c. 带有 virtual function的class

            d. 带有 vitual base class的子类


         至于没有存在这4种情况而又没有声明任何constructor的classes, 我们说它们拥有的是 implicit trivial default constructors, 他们实际上并不会被合成出来。


证明  a. 带有default constructor 的 member class object, 编译器会合成default constructor:

//a.cpp

class Foo
{
public:
    int val;
    Foo *pnext;
};

int main()
{
    Foo bar;

    std::cout << bar.val << std::end;  // bar.var 和  bar.pnext 是随机数,没被初始化为0
    return 0;
}

gcc -o a a.cpp;

nm -C a | grep Foo

输出结果为空。 


//a.cpp

class Foo
{
public:
    int val;
    Foo *pnext;

    string str;   //新增加的成员变量, string类有explict default constructor
};

int main()
{
    Foo bar;

    std::cout << bar.val << std::end;  // bar.var 和  bar.pnext 是随机数,没被初始化为0
    return 0;
}

gcc -o a a.cpp;

nm -C a | grep Foo   (或者 nm a | grep Foo| c++filt )

输出结果为:

00000000004008f8 W Foo::Foo()
0000000000400916 W Foo::~Foo()

上面的default constructor 和 destructor 函数就是编译器为我们合成的。 

        

2.2  copy Constructor的构造操作,什么时候被创建?

有三种情况下会用到copy constructor:

a.   class X {....};

       X x;

       X a = x;   // 有新对象实例生成会用到 copy constructor。 ( 如果没有新对象实例生成,会使用 operator =(), 例如 X x1, x2;  x1 = x2;)

b. 作为形参

    extern void foo(X x);

    void bar() {

    X a;

    foo(a);  //使用 copy constructor

    }

c.  作为函数返回值

     X foo_bar()

     {

           X a;

           ........

           return a;

     }

和default constructor一样,如果class没有声明一个copy constructor,就会有隐含的声明.  C++ 把copy construstor区分为 trivial 和 non-trivial两种。 只有non-trivial,编译器才会合成copy constructor到代码中。 但有一点需要注意,不管是 trivial 和 non-trivial的copy constructor,都会把整数,指针,数组等等的nonclass members 进行复制。


什么时候,才会合成copy constructor?

四种情况:

a. 当class 内含一个member object, 而这个object声明了一个non-trivial copy constructor。

b. 当class继承自一个base class, 而且 base class 存在一个 non-trivial copy constructor。

c.  当class声明了一个或者多个 virtual functions。

d. 当class 的父类中,有 virtual base class 时


证明:

//c.cpp

#include <iostream>
#include <string>
using namespace std;

class Test
{
public:
    Test(int i)
    {
        len = i;
    }
public:
    char *str;
    int len;
};

int main()
{
    Test a(5);
    Test b=a;
    Test c(10);
    Test d = b;
    std::cout << b.len << std::endl;
    std::cout << c.len << std::endl;
    std::cout << d.len << std::endl;
    return 0;
}

g++ -o c c.cpp; ./c  输出结果为:

5

10

5

sundh@SSDEV016:~$ nm -C c | grep Test  输出结果
0000000000400a0e W Test::Test(int)    //只有构造函数,没有 copy constructor, 

修改:

#include <iostream>
#include <string>
using namespace std;

class Test
{
public:
    Test(int i)
    {
        len = i;
    }
public:
    char *str;
    int len;
    string a;    //增加 string a成员变量, string是有non-trivial copy constructor。
};

int main()
{
    Test a(5);
    Test b=a;
    Test c(10);
    Test d = b;
    std::cout << b.len << std::endl;
    std::cout << c.len << std::endl;
    std::cout << d.len << std::endl;
    return 0;
}
sundh@SSDEV016:~$ nm -C c | grep Test  输出结果
0000000000400bb4 W Test::Test(int)
0000000000400bfe W Test::Test(Test const&)    //合成了copy constructor
0000000000400be0 W Test::~Test()


2.3  构造函数初始化列表

什么时候必须使用 初始化列表(member initialization list):

   a. 当初始化一个reference member时

   b. 当初始化一个 const member时

   c. 当调用一个base class 的constructor,而它拥有一组参数时(它没有 default constructor).  

   d. 当调用一个member object的constructor,而它拥有一组参数时(它没有 default constructor).  


陷阱:

class Word {

public:

    Word(){   //大括号里面的内容,叫做explicit user code

     strName = 0;   //会调用copy constructor, 但还会调用default constructor 吗?

     nCnt = 0;

     }

private:

     string strName;

      int nCnt;

};

//default constructor 扩展出来的 C++伪码

Word::Word(){

    strName.string::string();   //调用string的 default constructor

    string temp = string(0);  //新创建temp变量,调用string::string(int) 构造函数,然后调用copy constructor

    strName.string::operator=(temp);  // 调用string的 operator =() 操作符

    temp.string::~string(); // 调用析构函数


    nCnt = 0;

}

编译器会一一操作初始化列表(initialization list),以适当的次序在constructor之内安插初始化操作,并且在任何explicit user code(代码里面有说明)之前。 初始化列表中成员变量赋值顺序,有class中的声明顺序决定,而不是在list中的排列顺序。


总结:编译器会背着程序员干很多事情,大致顺序为:

a. 构造函数先调用base的构造函数,后设置其他变量,包括vptr;(相对于子类来说,如果base类有default construstor,后最先调用base的default construstor; 如果没有,需要程序员在子类的初始化列表显示调用base的带参数的construstor )

b. 构造函数初始化列表。 (如果列表里面包含了  base的构造函数, 那上面步骤a是初始化列表中第一个被执行的。 )

c. 构造函数调用虚函数,会调用它的vptr设置的虚函数,也就是说vptr的该虚函数。( this->__vptr = __vtbl_pointer; )

d. 执行explicit user code (前面代码里面有说明)。


可以参考:C++ - 对象模型之 构造和析构函数都干了什么              http://blog.csdn.net/gykimo/article/details/8629896


第三章   Data语意学 和   第四章 Function语意学

类有两种data member类型:non-static 和 static, 有三种 function member 类型: non-static , static 和 virtual。

继承中,有一种virtual base的虚继承。 

http://blog.csdn.net/sunny04/article/details/41700437    这篇文章详细讲解了,我就不赘述了。

需要注意的是:

1. BaseClass *temp = new SubClass();   这样操作,不仅仅是data member发生切割现象, vptr函数表也会发生切割现象, 只保留BaseClass的那一部分。

2. C++的设计标准之一是 类的nonstatic member function 需要和 一般的 non-member function效率相同。 如果确保这一点呢?  编译器将non-static member function 重新写成一个non-member function, 对函数名称进行 ‘mangling'处理,使它在程序中独一无二。

     class A{...

      int myPrint();

       }; 

      A *a = new A();

      a->myPrint() ;   //myPrint 这里是nonstatic 函数。  这行代码,会被编译器处理成:     myPrint_1AEv( this);     变成了一个non-member function,会隐形插入一个this形参。 

3. virtual member function 会被编译器这样处理:

         a->myPrint() ;     // myPrint 这里是virtual函数。  这行代码,会被编译器处理厂:   (* a->vptr[1])(a);   myPrint函数被slot的index取代


第五章   构造,解构,拷贝,语意学



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值