构造函数语意学----程序转化语意学

来源:互联网 发布:淘宝做代理发货怎么办 编辑:程序博客网 时间:2024/05/20 09:20
 显式的初始化操作
   已知这样的定义:
    X x0;
    下面的三个定义,每一个都明显的以x0来初始化其class Object:
    void foo_bar(){
        X x1(x0);
        X x2 = x0;
        X x3 = X(x0);
    //.....
    }
    
    必要的程序转化有两个阶段:
    1.重写每一个定义,其中的初始化操作会被剥除(这里所谓的“定义”是指上述x1,x2,x3三行,在严谨的C++用词中,定义是指占用内存的行为);
    2.class的copy constructor调用操作会被安插进去。
    
    举个例子:在明确的双阶段转化之后,foo_bar()看起来像这样:
    //可能的程序转化
    //C++伪代码
    void foo_bar()
    {
        X x1;//定义被重写,初始化操作被剥除
        X x2;//定义被重写,初始化操作被剥除
        X x3;//定义被重写,初始化操作被剥除
        //编译器安插 X copy constructor的调用操作
        x1.X::X(x0);
        x2.X::X(x0);
        x3.X::X(x0);
   // ......
    }
    其中x1.X::X(x0);
    就表现对以下的copy constructor的调用。
    X::X(const X& xx);


参数的初始化
    C++Standard说,把一个class Object当做参数传给一个函数(或是作为一个函数的返回值),相当于以下形式的初始化操作:
    X xx  = arg;
    其中xx代表形式参数(或返回值)而arg代表真正的参数值,因此已知这个函数:
    void foo(X x0);
    下面这样的调用方式:
    X xx;
    foo(xx);
    将会要求局部实例x0以memberwise的方式将xx当做初值。
在编译器实现技术上,有一种策略是导入所谓的临时性Object,并调用copy constructor将它初始化,然后此临时性Object交给函数。例如将前一段程序代码转换如下:
    //编译器产生出来的临时性对象。
    X __temp0;
    
    //编译器对copy constructor的调用
    __temp0.X::X(xx);
    //重新改写函数调用操作,以便使用上述的临时对象
    foo(__temp0);
    
    然而这样的转换只做了一半功夫而已。问题出在foo()的声明上。临时性Object先以class X的copy constructor正确的设定了初值。然后再bitwise方式拷贝到x0这个局部实例中。foo()的声明因而也必须被转化,形式参数必须从原先的一个class XObject改变为一个class reference,像这样:
    void foo(X& x0);
    其中class X声明了一个的destructor,它会在foo()函数完成之后被调用,对付那个临时性的Object。
    
返回值的初始化
已知下面这个函数定义:
    X bar()
    {
        X xx;
        //....
        return xx;
    }
    你可能会问bar()的返回值如何从局部对象xx拷贝过来?Stroustrup在cfront中的解决做法是一个双阶段转化:
    1.首先加上一个额外参数,类型是class Object的一个reference。这个参数将用来放置被“拷贝建构”而得的返回值。
    2.在return指令之前安插一个copy constructor调用操作,以便将欲传回之Object的内容当做上述新增参数的初值。

    真正的返回值是什么?最后一个转化操作会重新改写函数,使它不传回任何值。根据这样的算法,bar()转换如下:
    //函数转换
    //以反映出copy constructor的应用
    void bar(X &__result){
        X xx;
        //编译器所产生的default constructor调用操作
        xx.X::X();
        //....处理xx
        

        //编译器所产生的copy constructor调用操作
        __result.X::X(xx);
        return ;
    }

    下面三个初始化操作在语意上相等:
    X xx0(1024);
    X xx1 = X(1024);
    X xx2 = (X)1024;
    但是在第二行和第三行,语法明显地提供了两个步骤的初始化操作。
    1.将一个临时性的Object设以初值1024;
    2.将临时性的Object以拷贝构造的方式作为explicit Object 的初值。
    换句话说:
        xx0是被单一的constructor操作设定初值。
        xx0.X::X(1024);
    而xx1或xx2却调用了两个constructor,产生一个临时性Object,并针对该临时性Object调用class X的destructor:
        X __temp0;
        __temp0.X::X(1024);
        xx1.X::X(__temp0);
        __temp0.X::~X();
    
    named return value(NRV)优化
    

    Copy constructor:要还是不要?
    已知下面的3D坐标类:
    class Point3D{
        public :
            Point3D(float x,float y,float z);
            //...
        private:
            float x,y,z;
    };
    上述class的default copy constructor被视为trivial,它既没有任何member(或base)class Object带有copy constructor,也没有任何virtual base class或virtual function,所以默认情况下,一个Point3D class object的memberwise初始化操作会导致“bitwise copy”。这样效率很高,很安全。bitwise copy既不会导致memory leak也不会产生address aliasing.

    如果被问及是否预见class需要大量的memberwise初始化操作,例如以传值(by value)
的方式传回Object,那么提供一个copy constructor的explicit inline函数实例就非常合理。
        
    例如:
    Point3D operator+(const Point3D&,const Point3D &);
    .......
    所有那些函数都能够良好的符合NRV Template
    {
        Point3D result;
        //计算result
        return result;
    }  
    实现copy constructor的最简单方法像这样:
    Point3D::Point3D(const Point3D &rhs){
        x = rhs.x;
        y = rhs.y;
        z = rhs.z;
    }
    使用C++ library的memcpy()会更有效率:
    Point3D::Point3D(const Point3D &rhs){
        memcpy(this,&rhs,sizeof(Point3D);
    }
    然而不管使用memcpy还是memset,都只有在classes不含任何由编译器产生的内部members时才能有效运行。如果Point3D class声明一个或一个以上的virtual functions或内含一个virtual base class,那么使用上述函数就会导致哪些被编译器产生的内部member的初值被改写。
    class Shape{
        public :
            //改变内部vptr
            Shape(){ memset(this,0,sizeof(Shape));}
            virtual ~Shape();
            //.....
      };
    编译器为此constructor扩张的内容看起来像这样:
    //扩张后的constructor
   Shape::Shape(){
        //vptr必须在使用者的代码执行之前先设定妥当
        __vptr__Shape = __vtbl__Shape;

        //memset 会将vptr清为0
        memset(this,0,sizeof(Shape));
    }
0 0
原创粉丝点击