• 《深入探讨C++对象模型》笔记 二


    构造函数语意学

    1,默认构造函数(default constructors)是在需要的时候被编译器产生出来。这里需要理解的是被谁需要,产生出来做什么事情。分析下面这段代码

     9 class foo
    10 {
    11 public:
    12     int m_nval;
    13     foo *pnext;
    14 };
    15 
    16 void foo_bar()
    17 {
    18     foo bar;
    19     if (bar.m_nval 
    20         || bar.pnext != NULL)
    21     {
    22         // do something
    23         cout<<"do something..."<<endl;
    24     }
    25 }

      从上面的例子中,可以看出foo_bar()函数中定义了一个foo类对象bar,当这个对象bar初始化后的两个成员变量被赋值时输出一句话。上面这段代码编译器会产生出一个默认构造函数(default constructors)吗?答案是不会。原因在于一个是程序的需要,一个是编译器的需要。程序的需要是通过程序员来编写构造函数的,而编译器的需要是通过编译器默认的产生出来的。

      只有4种情况,编译器才会产生默认构造函数,这种构造函数被视为是一种nontrivial default constructors(有用的默认构造函数)。这4种情况分别是:

    1,“带有default constructor”的Member class object。

    2,“带有default constructor”的base class

    3,“带有virtual function”的class

    4,“带有一个virtual base class”的class

    下面分别介绍这几种情况

    1,“带有default constructor”的Member class object,例子:

     1 class Foo
     2 {
     3 public:
     4     Foo(){};
     5     Foo(int nval){ m_nval = nval;};
     6 public:
     7     int m_nval;
     8 };
     9 
    10 class Bar
    11 {
    12 public:
    13     Foo foo; // 带有default constructor”的Member class object
    14     char *str;
    15 };
    16 
    17 void foo_bar()
    18 {
    19     Bar _bar;//Bar::foo函数成员对象 必须在此处合成出来
    20     if (_bar.str != NULL)
    21     {
    22         cout<<"do something..."<<endl;
    23     }
    24 }

    上面这段代码,编译器为class Bar合成一个default constructor, 被合成出来的default constructor函数体内含有必要的代码能够调用class Foo的默认构造函数

    Foo(){};来处理Bar中的Bar::foo,但是这个default constructor不产生任何的代码来初始化Bar::str成员函数,因为将Bar::foo初始化是编译器的责任,将Bar::str初始化则是程序员的责任。
      被编译器后台合成出来的default constructor应该类似于:
    class Bar
    {
    public:
        Foo foo;
        char *str;
    public:
        inline Bar();
    };
    
    inline Bar::Bar()
    {
        foo.Foo::Foo();//伪代码
    }

       再次强调,被编译器后来合成出来的构造函数只是用来满足编译器的需要,而不是程序的需要。

      为了能够让上面程序正确的执行,字符指针*str也需要被初始化,这需要程序员提供一个构造函数来对这个指针进行初始化,初始化构造函数如下:

    Bar::Bar()
    {
        str = "构造实例";
    }

      现在程序的需求满足了,但是编译器还需要初始化Bar::foo对象,由于默认构造函数已经明确的被定义出来了,编译已不能合成默认的构造函数,这个时候编译器会采取这种方式“如果class A 内含有一个或一个以上的member class object,那么class A的每一个constructor必须调用每一个member class object的default”。这时编译器会扩张已存在的构造函数,在其中安插一些代码,使得user code在被执行之前,先调用必要的default constructor.。扩张后的代码类似于:

    Bar::Bar()
    {
        foo.Foo::Foo();//编译器扩张的代码,伪代码
        str = "构造实例";
    }

      对于含有多个memeber class object。C++语言要求按照这些成员类对象声明的次序来调用各个的constructor。

    2,“带有default constructor”的base class

      类似的道理,如果一个构造函数的类派生自一个带有默认构造函数的类,那么这个派生类在创建对象的时候,编译器隐含的合成一个默认构造函数。原理同1类似。

    3,“带有virtual function”的class

      另有两种情况也需要编译器合成默认构造函数,1:class声明或者继承了一个虚函数,2.class派生自一个继承串链,其中有一个或者多个虚基类。

    不管哪一种情况,由于没有人为的声明一个构造函数,那么编译器需要自己合成一个默认的构造函数。

    4,“带有一个virtual base class”的class

      上面四种情况,会使编译器必须为一个未 人为声明构造函数的类 去合成一个默认的构造函数,被合成出来的默认构造函数只能满足编译器自己的需要。对于没有存在这四种情况并且又没有人为的声明构造函数的类,他们的默认构造函数不会被合成出来。

      常见的两种误解:

      1,任何class如果没有定义构造函数,就会被合成出来一个

      2,编译器合成出来的默认构造函数会明确的设定class内的每一个数据成员的默认值。

      这两种想法都是都是错误的

  • 相关阅读:
    《JavaScript》forEach()和map()
    《JavaScript》split和join
    09慕课网《进击Node.js基础(一)》HTTP-get/request
    08慕课网《进击Node.js基础(一)》事件events
    07慕课网《进击Node.js基础(一)》HTTP小爬虫
    06慕课网《进击Node.js基础(一)》作用域和上下文
    05慕课网《进击Node.js基础(一)》HTTP概念进阶(同步/异步)
    前端每周学习分享--第4期
    前端每周学习分享--第3期
    前端每周学习分享--第2期
  • 原文地址:https://www.cnblogs.com/aimenfeifei/p/4252900.html
Copyright © 2020-2023  润新知