• 第二章构造函数语义学——关于构造函数和复制构造函数


    以下代码体现了书中所说的如下几个知识:

    1. 如果一个class没有任何constructor,但它含有一个member object, 而后者有default constructor,编译器需要为该class合成出一个default constructor。(P41)

    2. 如果 类中的default constructor已经被显示的定义出来,编译器没办法合成第二个,编译器会采取什么行动呢。编译器会扩张已存在的constructors, 在其中安插一些代码,使得 user code 被执行前,先调用必要的default constructors。(P42)

    3. 当class内含一个menber object 而后者的class声明中有一个copy constructor时,不会出现bitwise copy semantics,编译器必须合成一个copy constructor,将members的copy  constructors调用操作,安插到被合成的copy constructor。(P53)

    4. 合成复制操作符(synthesized assignment operator)会执行逐个成员复制(memberwise assignment)。(C++ primer P411)。

    所用的代码如下:

    class String// 注意这个String不是容器类的那个,那个是string
    {
    public:
        String(){
            cout<<"String()"<<endl;
            len = 5;
        }
    
        String(const String& o){
            cout<<"String(const String& o)"<<endl;
            len = 10;
        }
    
        int len;
    
    };
    
    class Word
    {
    public:
        Word(){  
            cout<<"Word()"<<endl;
        }
        Word(const String& o){
            cout<<str.len<<endl;////注意这里的输出
            cout<<"Word(const String&)"<<endl;
            str = o;
        }
    public:
        int cnt;
        String str;
    };
    
    int main(int argc, char* argv[])  
    {  
    
        String o;//调用String的默认构造函数
        o.len = 20;
        cout<<endl;
        Word w = o;//调用复制构造函数进行构造
        cout<<endl;
        w.cnt = 100;
        Word w2= w; // 调用复制构造函数,因为没有对应了,则编译器会帮我们产生一个
        cout<<endl;
        cout<<"w.str.len = "<<w.str.len<<endl;
        cout<<"w2.str.len = "<<w2.str.len<<endl;
        cout<<"w2.cnt = "<<w2.cnt<<endl;
        return 0;
    }  

    输出的结果:

    String()

    String()
    5
    Word(const String&)

    String(const String& o)

    w.str.len = 20
    w2.str.len = 10
    w2.cnt = 100


    根据main中的内容以及结果进行分析:

    String o;
    //定义个String类的对象o,这里因为String显示定义了构造函数,所以会调用String::String(),结果就是输出String(),以及将 o.len 赋值为5

    o.len = 20;// 没啥可说的,将o.len从5赋值为20

    Word w = o;
    // 这里看到Word中有一个构造函数Word(const String& o) 则调用该函数,但是根据我们的第一个和第二个知识点,编译器看到类中有一个成员变量中包含了构造函数,那么编译器会在调用该函数之前,插入代码调用 该成员变量的构造函数,也就是说,在cout<<str.len<<endl之前,编译器会插入一段代码调用str的构造函数,这样也就会将str.len赋值为5。这样在调用构造函数的时候,会输出String(),在执行cout<<str.len<<endl的时候,会输出5,之后执行cout<<"Word(const String&)"<<endl;输出Word(const String&),下一句代码为str=o,这里因为没有重载运算符=,编译器会为我们合成复制操作符,它会执行逐个成员赋值(C++ primer P412)。这里会将str.len赋值为20

    //这里这个函数成为 构造函数,还是称为复制构造函数,还有点问题。C++ primer P406中对于复制构造函数的定义是:只有单个形参,而且该形参是对本类类型对象的引用。在《深度》P48页的Copy Constructor的构造操作中,将等号作为了一种显示的调用方式,而且用的是如下的描述: 以一个object的内容作为另一个class object的初值。

    w.cnt = 100

    Word w2 = w;
    // 这里因为没有定义对应的复制构造函数,所以编译器会帮我们定义一个,并且进行memberwise initialization,先用w.cnt初始化w2.cnt,这样w2.cnt就变成了100,在用w.str初始化w2.str,这里会调用String的复制构造函数,也就是会输出Word(const String&),这样就完成了构造。所以w2.str.len是在String的复制构造函数中进行赋值的,也就是10

    // 这里如果先构造出w2,在通过w2=w,进行复制的话,w2.str.len就会等于w.str.len

    之后输出。

        cout<<"w.str.len = "<<w.str.len<<endl;
        cout<<"w2.str.len = "<<w2.str.len<<endl;
        cout<<"w2.cnt = "<<w2.cnt<<endl;

    w.str.len 是在Word(const String& o)中先被赋值为5,然后在通过 w.str=o,赋值成了o.len也就是20

    w2.str.len 是通过编译器生成的 复制构造函数中,调用了String的显示复制构造函数中,进行赋值的,赋值为10

    w2.cnt是通过编译器生成的 复制构造函数中进行了赋值,赋值为100。

    所以最后的输出是这样的。


    编译器真的辛苦的说啊。

  • 相关阅读:
    IntelliJ IDEA 快捷键终极大全,速度收藏!
    49式!Python初级到高级招式都全了
    字节跳动三轮技术面_后端研发提前批
    这样让你的 IDEA 好用到飞起来!
    你 多久没有跳槽了?
    推荐几款能提升代码效率的笔记应用
    推荐 15 款编程游戏,从此谁都可以学编程!
    StackOverflow 创始人关于如何高效编程的清单
    11 条编程经验分享
    InnoDB和MyISAM存储引擎的区别
  • 原文地址:https://www.cnblogs.com/cyttina/p/2781399.html
Copyright © 2020-2023  润新知