构造函数和析构函数

目录

页面

构造/和析构函数


问题

编译器为什么要求拷贝构造为什么要传引用?

如果存在两个 同类型的对象A a, b ,如果将a 拷贝给 b ,实际上call b类的拷贝构造,b对象的拷贝对象是如果是非引用形式。以 a() 实参, (A const A a)形参 ,这时是同一类型,他又回去call 形参的拷贝构造,形参还是(A const A a)形参 ,实参又去形参的拷贝构造 ,套娃。。。

Int & 不能拷贝给匿名对象上的

为什么要const A &a (45 被bind 后是没有绑定匿名对象上的,const 是为兼容bind 到匿名对象的 )

做一个逻辑上的常量限制。(从逻辑上是不允许被拷贝对象被修改的)

兼容const类型的拷贝 不允许直接吧const 类型的变量直接传到址非const的引用, 因为非const 是可以修改的。


构造函数与析构函数

构造/和析构函数 使用方式
默认构造函数 ostream a;
ostream(string name ) ostream a(xx)
ostream (const ostream &a) 拷贝构造 于 = 不等价
~ostream 无 析构

每个对象创建时,一定call 构造函数,当然销毁也一定会调析构。


class Data{
public :
    Data() {
        cout << "default " << endl;
    }   
    int  x(){return __x;}; 
    int  y(){return __y;};

    ~Data(){
        cout  << "Free" <<endl;
    }   
private :
    int __x,__y;
};

int main(){
    Data dt; 
    cout <<dt.x() << dt.y() <<endl;                                                
    return 0;
}
// outoput 
//default 
//41969760
//Free

初始化列表

(初始化类中的成员)

初始化列表的顺序,和类中属性,声明的顺序有关系。而不是由初始化列表的顺序决定

最终的意义就是→初始化每一个属性的行为。

按照约定,如果构造函数初始化完成所有的初始化列表 ,意味每个成员对象的属性已经初始化结束。

class A{
public :
    A(){} // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 
    A(int x):x(x){
        cout << "Class A:" << x  <<endl;
    }   
    int x ; 
};

class Data{
public :
    // 接在构造函数后面  : 初始列表(初始化成员)
    //显示性的调用 A的构造函数
    Data():__x(10),__y(0),a(34) {
        cout << "default " << endl;
    }   
    int  x(){return __x;}; 
    int  y(){return __y;};

    ~Data(){
        cout  << "Free" <<endl;
    }   
private :                                                                          
    int __x,__y;
    A a;
};

转换构造

一个参数构造函数又转换构造

这个赋值运算可以看做 将int转到A类型的对象,整形转换为一个A类型的对象.

class A{
  public :
      A(){} // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 需要显示指出构>      A(int x):x(x){
          cout << "Class A:" << x  <<endl;
      } 
      int x ;
 };
 int main(
   A s(45);   
   A x =41; 
   return
 )
 
//0x7fff1a506a40  Class A:45
// addressof a :0x7fff1a506a40

拷贝构造(这种都是浅拷贝,每一项成员依次拷贝过去)

系统会自动添加拷贝构造,即使没有声明拷贝构造.在调用时,也会依次调用

☕g++ -fno-elide-constructors 构造函数返回值优化 编译时需要带上参数才能编译出


class A{
public :
    A(){
        cout << " default" << endl;
    } // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 需要显示指出构造函数
    A(int x):x(x){
        cout << this << "  Class A:" << x  <<endl;
    }   
    A(const A &a){
        //拷贝构造
        cout << this << "   :copy A" << &a <<endl; 
    }   
    int x ; 
};

 A a=45;
 cout << "addressof a :" <<  &a <<endl;

0x7ffcfaaf6380  Class A:45
0x7ffcfaaf6370   :copy  from A0x7ffcfaaf6380
addressof a :0x7ffcfaaf6370

第一行:调用为6380 的有参构造 ,这个小a的地址为 6370,也就是说这个0x7ffcfaaf6380 并非调的小a的有参构造

第二行,在调用第一行的有参构造函数后,第二行也会默认执行拷贝构造 ,将f6370拷贝给小a

☕首先这个**45 **会通过转换构造 去转换成一个匿名对象,匿名的对象为这个f6380对象,然后再将匿名的a对象拷贝6370对象

默认的赋值运算符

当赋值运算符出现的时候,起始出现的是拷贝构造 ,因为 这个a 还没声明好,所以使用的是拷贝构造,

而下面的而赋值运算符在赋值时,a 对象已经准备好了

class A{
public :
    A(){
        cout << " default" << endl;
    } // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 需要显示指出构造函数
    A(int x):x(x){
        cout << this << "  Class A:" << x  << endl;
    } 
    A(const A &a){
        //拷贝构造
        cout << this << "   :copy A" << &a << endl; 
    }
    void operator=(const A &a){
        cout << this << "assign from " << &a << endl;
    }
    int x ;
};
0x7ffef092db80  Class A:45
0x7ffef092db70   :copy from  A0x7ffef092db80
addressof a :0x7ffef092db70
0x7ffef092db90  Class A:78
0x7ffef092db70   assign from 0x7ffef092db90

int main(){
   A a = 45;
   cout << "addressof a :" <<  &a <<endl;
   a  =  78 ; //这个= 是相当于是call赋值运算符
   return 0;
 }


db80 通过转换构造 构造的对象 db90 也通过转换构造 构造的对象 (匿名的对象)

这个a的对象为 db70 通过 db80 拷贝过来 || 而这个db90 是通过赋值运算符赋值给 db70的

小的总结

A a = 78 由于这里的等号 ,一定call 调了拷贝构造 (而拷贝的出现的形式,是用的A类型的引用,这时会将45的参数(它本就不是a类型的值). 这时会通过转换构造,将45转换为匿名的临时对象,然后绑定到这个const A &a 的引用上)

a = 78 ; //这里就很明显,就调了 赋值运算符 (const A &a),后续过程一致。