0%

cpp_conandde

c++ class constructor and destructor

构造函数

构造函数表现

构造函数的作用:

构造函数主要是用来初始化对象的---一般是成员,函数不用
–所以它需要在构建对象时就执行

构造函数如何写:
public: classname(arg...):member(arg),mem2(arg),..{xxx}
    classname(..){..}
默认构造函数的生成规则(深入对象模型)
  • 下面四个情况下会生成默认构造函数(编译器控制)(nontrivial default constructor)

  • 带有default constructor的member class object
    即在类中带有对象成员,该对象成员所属的类有构造函数
    如: 

    class Foo {public :Foo(),Foo(int)...}
    class Bar{public:Foo foo;char *str};
                           void funv()
                           {   Bar bar;....
                           }```
    这种情况下若程序员没有实现构造函数,则编译器会自己生成构造函数,来调用Foo的构造函数
    但是不初始化str,这个得由程序员来做。
    如类似于:
    ```cpp
    Bar::Bar(){
    foo.Foo::Foo();
    

    若程序员自己写了构造函数但是不包括初始化foo则,编译器会在程序眼写的构造函数中加入上述,另外若有多个对象
    则按照声明顺序进行调用他们的构造函数;

  • 带有Default constructor 的base class
    同理,当继承的基类带有构造函数时就会加入,若程序员的构造函数没有初始化他们时就会扩展
    //以上见例子class_constructor.cpp,

  • 和vcirtual相关need to create vptr
    带有一个virtual func的class
    1)class 声明或继承一个virtua func
    2)class派生自一个继承琏,其中有virtual base classes 这个比较容易理解

  • 和virtual相关 need to init vptr
    带有一个virtual base class 的class
    如:
    cpp class X{ public int i;} class A :public virtual X {/// class B :public virtual X { class C: public A,public B

构造函数何时被执行

-在对象定义时若有构造函数,则会执行

实践:

1:5构造函数的使用:重载,初始化式

构造函数表现的原理

构造函数在静态代码块中的位置和符号体现

如果有定义构造函数,它本身也是一个函数,放在代码段中,可以通过kdbg查看

origin(int ax=3,int bx=4):a(ax),b(bx){}
0x400bae push   %rbp
0x400baf mov    %rsp,%rbp
0x400bb2 mov    %rdi,-0x8(%rbp)
0x400bb6 mov    %esi,-0xc(%rbp)
0x400bb9 mov    %edx,-0x10(%rbp)```
##### 构造函数在动态执行时,放在哪个内存段中,如何被引用,使用
+ 动态执行时,在代码段中,通过this引用
+ 构造函数总结:任何c++ class只有在以上四中情况下才会产生默认构造函数,且产生的默认构造函数不会去初始化成员的值
### 拷贝构造函数
#### 拷贝的动作发生了什么
拷贝的本质,为什么需要拷贝构造函数?
+ 首先对基本的类型而言,拷贝是把值赋给另一个同类型的变量,而数租不能做=赋值拷贝
+ 对对象而言,也是把值赋给同类型的对象,而不是共享一个指针,所以这个过程需要将对象中的成员也复制过去(复制的是当前对象在此时时成员的值 )
+ c++中的拷贝构造函数针对三种行为:= ,f(T t)和 f(){T t;return t}返回对象--这三种情况都针对左值的   
+ =:注意这个是在定义时做的,如origin or1=or2;此时会调用"拷贝构造函数"( 同or1(or2))
 or2=or3;此时不会调用拷贝构造函数
(当程序员自己写了拷贝构造函数时,默认调用该构造函数,而不会区做成员复制,所以若在其中没有做复制,可能得到不想要的值,见例子)

#### 拷贝构造函数的作用和使用
##### 什么情况下会生成默认的拷贝构造函数?
类似于构造函数,在以下情形会生成默认的拷贝构造函数-
+ 当需要调用别的拷贝构造函数时,如:当类内含有一个member object而后者的class声明有一个copy constructor时;
+ 当类继承自一个base class而后者有拷贝构造函数
+ 当类声明了一个或多个virtual functions时
+ 当class 派生自一个继承 串琏,而其中有一个或多个virtual base classes 时

#### 拷贝构造函数和编译器---汇编,转换:
分三种情况讨论:
+ 初始化拷贝构造:
```cpp
                   X x1(x0);
                   X x2=x0;
                   X x3=X(x0);```
   上述三种都是定义一个类,即定义的本质会在内存中开辟空间
上述三个都会执行拷贝构造函数,如何执行?  
会被转换为:伪代码
        X x1;
        x1.X::X(x0);
        会调用X::X(const X& xx) 
        x2,x3也是这样,将拷贝方作为函数参数传入
        这样就可以解释为什么拷贝构造函数的定义是      
        classname (const classname &obj)
+ 参数的初始化
即传入一个参数给函数:
       foo(X x)
如:
       X xx;
       //,..
       foo(xx);
       则会产生一个临时的对象:
 伪代码
        X __temp0;
        _temp0.X::X(xx);//use copy construtor
         foo(__temp0)
这里因为它是临时的,所以则定义的时候需要用引用foo(X &x)//这里不是很清楚为什么用引用~!,是否后面的操作直接面向该对象,需要做实际的修改,所以。。
所以,为了对付临时的对象,在执行完函数后,需要调用该对象的析构函数
+ 返回值的初始化:  
如
       X bar(){
                      X xx;
                       //...
                       return xx;
            }
         如何做X xxx=bar();如何拷贝的?双阶段初始化:
         a 增加一个额外的引用参数给函数,如void bar(X& _result)
         b 在return 前插入一个copy constructor 
             void bar(X &__result){
                       X xx;
                       xx.X::X();
                       __result.X::XX(xx);
                       return ;
               }
        所以上述会被转化为:
        X xx=bar()  --->  X xx ;//注意这里不会执行默认构造函数  bar(xx);
            ex:bar().memfunc()--->X __temp0;(bar(__temp0),__temp0).memfunc();
            X (*pf)();pf=bar;--->void (*pf)(X&);pf=bar;
            
##### 关于上述三种情况的优化:
1)在使用者层面上优化;2)在编译器层面的优化(NRV)具体见深入模型(书)
##### 关于该不该编写copy  constructor: 
除非需要大量的memberwise 初始化操作,如传值给object以便做NRV优化,否则不需要
//上述情况的检验可以通过代码,或者去看编译器的生成代码~    
##### 拷贝构造函数的内存
放在代码段,
### 初始化队列
即构造函数的一种形式如:X(int f):a(ax),b(bx)..{....}
+ 问题:什么时候用初始化列表?它和初始化赋值有什么不同?
        有以下四种情况需要使用初始化列表:
        1)当初始化一个reference member时
        2)当初始化一个const member时
        3)当调用一个base class的constructor,当它拥有一组参数时
        4)当调用一个member class 的consructor,而它有一组参数时;
如
``cpp
   class world {
          String _nhame;
          int cnt;
          public :world(){_nhame=0;cnt=0;}}```
//此时,对_nhame这个成员,需要做较多的事,如产生临时对像,调用operator=....
   ----所以想到用初始化列表:
```cpp 
          world::world:nhame(0){
            cnt=0;
            }
            这样只会调用nhame的构造函数
            会被转换为:world::world{ //伪代码
             _nhame.String::String(0);
              cnt=0;
              }```
+ 更具体的,初始化列表如何进行:顺序如何:如前,初始化列表最终会被转换为初始化表达式到构造函数体中,那是什么顺序呢?  
注意这里的初始化顺序是根据成员在类中的定义(声明)顺序来的,而不是初始化列表中的顺序
看这个例子:
```cpp 
       class X{
                    int i; 
                    int j;
                   public:
                     X(int cal):i(j){}
                     ...
                     此时,因为i先初始化,再j,出错,i需要j```
                     
  ---->可以改善为:X::X(int cal):j(cal){i=j;}   
   ----为什么这样可以?  
   因为初始化列表的内容会放到初始化体中的表达式前面,这样j就会先定义
        ----》另一个可能出错的例子:
           X::X(int cal):i(xfoo(cal)),j(cal){}
        -->转换为:X::X(/*this pointer*/ int cal){
                      i=this->xfoo(cal);
                      j=cal;
                      }
            这个情况在这里是对的,但是当X 继承自某个类,在xfoo是基类的函数,,则会出错this->xfoo

#### 几个问题:
+ 较为简单的例子见文件中的例子
+ 当程序员定义了拷贝构造函数时,还会执行memberwise initialization吗 --会执行拷贝构造函数
参考:深入c++对象模型和http://en.cppreference.com/w/cpp/language/copy_constructor