• 【深度探索C++对象模型读书笔记】【第2章】构造函数语意学


    一、Default Construct的构造操作

    1、 C++ Standard说:对于一个类,如果没有用户定义的构造函数,那么会有一个默认构造函数被隐式声明出来,但这个构造函数是trivial constructor(没啥用的)。

    2、 以下4种情况编译器会合成nontrivial default construct:

    a)  带有default constructor的member class object

    如果一个class没有任何constructor,但它内含一个member object,而后者有default constructor,那么编译器需要为该class合成一个default constructor。

    如果一个class内含一个或一个以上的memberclass objects,那么这个class的每一个constructor必须调用每一个member class的default constructor。编译器会扩张已存在的constructors,在其中安插一些代码,使得user code被执行之前先以“member objects在class中的声明顺序”来调用各个default constructors。

    例子:

    class Dopey{ public:Dopey(); ...};
    class Sneezy{ public:Sneezy(int); Sneezy(); ... };
    class Bashful{ public:Bashful(); ... };
    
    class Snow_White{
    public:
    	Dopey dopey;
    	Sneezy sneezy;
    	Bashful bashful;
    	// ...
    private:
    	int mumble;
    };
    
    Snow_White::Snow_White():sneezy(1024){
    	mumble = 2048;
    }
    // 编译器扩张后的default constructor
    Snow_White::Snow_White() : sneezy(1024)
    {
    	// 插入member class object
    	// 调用其constructor
    	dopey.Dopey::Dopey();
    	sneezy.Sneezy::Sneezy(1024);
    	bashful.Bashful::Bashful();
    	// explicit user code
    	mumble = 2048;
    }
    

    b)   带有default construct的base class

    如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。

    c)   带有一个virtual function的class

    下面两个扩张操作会在编译期间发生:

    1.一个virtualfunction table会被编译器产生出来,内放class的virtual functions地址;

    2.在每一个classobject中,一个额外的pointer member(也就是vptr)会被编译器合成出来,内含相关的class vtbl的地址。

    d)   带有一个virtual base class的class

    3、 在合成的default constructor中,只有base class subobjects和member class objects会被初始化,所有其它的nonstaticdata member(如整数、整数指针、整数数组等等)都不会被初始化。

    二、Copy Construct的构造操作

    1、 有三种情况,会以一个class的内容作为另一个class object的初值:

    a)  对一个object作显示的初始化操作,如:X x2= x1;

    b)  一个object被当作参数交给某个函数时,如:foo(x2);

    c)  当函数返回一个class object时,如return x2。

    2、 如果class没有提供一个explicit copy constructor,当class object以相同class的另一个object作为初值时,其内部是以所谓的default memberwise initialization手法完成的,即把每一个内建的或派生的data member的值,从一个object拷贝到另一个object。不过它并不会拷贝其中的member class object,而是以递归的方式施行memberwise initialization。

    3、 default constructors和copy constructors在必要的时候才由编译器产生出来。“必要”意指当class不展现bitwisecopy semantics时。没有default constructors和copy constructors时,编译器通过bitwise copy来产生类,就是将源类中的成员变量中的每一位都逐次复制到目标类中。这样导致如果类中的成员变量有一个指针,则只拷贝了指针值,两个指针指向同一块内存。

    #include<iostream>
    using namespace std;
    
    class Word{
    public:
    	Word(const char* s){
    		str = new char[strlen(s) + 1];
    		strcpy(str, s);
    		cnt = strlen(s) + 1;
    	}
    	~Word(){ delete[] str; }
    public:
    	char* str;
    	int cnt;
    };
    
    int main(){
    	Word noun("book");
    	Word verb = noun;
    
    	cout << noun.str << endl;
    	cout << noun.cnt << endl;
    	cout << static_cast<void*>(noun.str) << endl; //两个对象的str指针指向同一块内存
    	cout << static_cast<void*>(verb.str) << endl;//两个对象的str指针指向同一块内存
    
    	system("pause");
    	return 0;
    }
    

    4、  以下4种情况,一个class不展现bitwisecopy semantics:

    a)   class内含一个member object而后者的class声明或被编译器合成有一个copy constructor时;

    b)   当class继承自一个base class而后者存在或被编译器合成有一个copy constructor时;

    c)   当class声明了一个或多个virtual functions时;

    d)   当class派生自一个继承串链,其中有一个或多个virtual base classes时。

    5、 编译器对于每一个新产生的class object的vptr不能成功而正确地设好其初值,将导致可怕的后果。当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics

    6、  当一个base class objec以其derived class的object内容做初始化操作时,其vptr复制操作也必须保证安全。

    7、  每一个编译器对于虚拟继承的支持承诺,都代表必须让derived class object中的virtual base classsubobject的位置在执行期准备妥当。维护位置的完整性是编译器的责任。

    三、程序转化语意学

    1、  显示的初始化操作都有两个必要的程序转化阶段:

    a)   重写每一个定义,剥除其中的初始化操作;

    b)   安插classcopy constructor调用操作。

    2、 把一个class object当作参数传给一个函数或是作为一个函数的返回值,相当于以下形式的初始化操作:X xx = arg; 其中xx代表形式参数或返回值,而arg代表真正的参数值。形参必须从原先的类的对象改变为类的引用,避免复制。

    3、  函数定义如下:

    X bar(){
    	X xx;
    	//处理xx……
    	return xx;
    }
    

    bar()的返回值通过一个双阶转化从局部对象xx中拷贝出来:

    a)  首先添加一个额外参数,类型是class object的一个reference,这个参数用来放置被拷贝构建而得的返回值。

    b)  return指令之前安插一个copy constructor调用操作,以便将欲传回之object的内容当作上述新增参数的初值,同时重写函数使它不返回任何值。

    bar()转换如下:

    void bar(X& _result){
    	X xx;
    	//编译器所产生的default constructor调用操作
    	xx.X:X();
    	//......处理xx
    	//编译器所产生的copy constructor调用操作
    	_result.X::X(xx);
    	return;
    }
    

    4、  Named Return Value(NRV)优化如今被视为是标准C++编译器的一个义不容辞的优化操作,特点是直接操作新添加的额外参数。只有copy constructor的出现才会激活C++编译器的NRV优化。NRV优化虽然极大地改善了效率,但还是饱受批评:一是优化由编译器默默完成,而是否完成以及其完成程度完全透明;二是一旦函数变得比较复杂,优化就变得比较难以施行;三是优化由可能使程序产生错误——有时并不是对称地调用constructor和destructor。

    四、成员们的初始化队伍

    1、 必须使用member initialization list

    a)  初始化一个reference member时;

    b)  初始化一个const member时;

    c)  调用一个base classconstructor,而它拥有一组参数时;

    d)  调用一个member classconstructor,而它拥有一组参数时。

    2、 编译器会一一操作initialization list,以适当顺序在constructor内安插初始化操作,并置于任何explicit user code之前。

    3、 initialization list的项目顺序是由class中的members声明顺序决定的,不是由initialization list中的排列顺序决定的。

    #include<iostream>
    using namespace std;
    
    class X{
    private:
    	int i;
    	int j;
    public:
    	X(int val) :j(val), i(j){}
    	void printij(){
    		cout << i << endl; //乱七八糟的一个数
    		cout << j << endl; //5
    	}
    };
    
    int main(){
    	X x(5);
    	x.printij();
    
    	system("pause");
    	return 0;
    }

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    C语言I博客作业04
    PTA一般问题汇总与解答
    C语言I博客作业03
    C语言I博客作业02
    C语言I—2019秋作业第一周作业
    C语言I博客作业03
    C语言I博客作业02
    第一周作业
    【2017下集美大学软工1412班_助教博客】团队作业8——测试与发布成绩公示
    《构建之法》读书笔记第8章——需求分析
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4785454.html
Copyright © 2020-2023  润新知