• 构造函数_析构函数_深拷贝与浅拷贝


    拷贝构造函数为什么要使用引用而不是值传递???

      CExample aaa(2);   

      CExample bbb(3); 

     assignment operator                // bbb = aaa;

     copy constructor                      // CExample ccc = aaa;

    bbb对象已经实例化了,不需要构造,此时只是将aaa赋值给bbb,只会调用赋值函数opetator=;

    但是ccc还没有实例化,因此调用的是拷贝构造函数,构造出ccc,而不是赋值函数.

    构造ccc,实质上是ccc.CExample(aaa); 我们假如拷贝构造函数参数不是引用类型的话, 那么将使得 ccc.CExample(aaa)变成aaa传值给ccc.CExample(CExample ex),即CExample ex = aaa,因为 ex 没有被初始化, 所以 CExample ex = aaa 继续调用拷贝构造函数,接下来的是构造ex,也就是 ex.CExample(aaa),必然又会有aaa传给CExample(CExample ex), 即 CExample ex = aaa;那么又会触发拷贝构造函数,就这下永远的递归下去......

    C++提供构造函数和析构函数用于 数据成员的初始化和清理。

    构造函数:

    定义:

    1、C++定义与类名相同的特殊成员函数,即构造函数。

    2、有参或无参。

    3、无返回类型。

    调用:

    自动调用,C++编译器会自动调用构造函数;

    手动调用:在一些情况需要手动调用。

    先后定义两个对象T1和T2,构造函数先调用T1后调用T2,析构函数倒序调用,先调用T2后调用T1。

    无参构造函数:

    在类外定义对象后,自动调用无参构造函数,并在内部进行对象数据成员的赋值;

    有参构造函数:

    括号法:Text T1(3,4);

    等号法:Text T2 = (1,2,3,4);   //逗号表达式将最右边的数字返回。一个参数

    手动调用:Text T3 = T1(3,4);  //对象的初始化,调用拷贝构造函数

    Text T4 ;   T4 = T3;    //对象的赋值  调用的是C++自带的 operator = () 或者用户重载的等号操作函数。

    重要:对象的初始化 与 对象的赋值 是不同的概念。C++编译器分别调用的是拷贝构造函数和等号操作符。

    拷贝(copy)构造函数:用1个对象去初始化另一个对象 :

    1、Text T2 = T1;      //注意对象初始化使用的是拷贝构造函数

    2、Text T2(T1);

    3、 调用f(T2)时,C++编译器调用拷贝构造函数,用T2完成p的初始化。此时,p是一个与T2等价的元素(非指针,非引用),f()函数结束,元素p被析构。

    void f(Text p)
    {
         cout << p.fun() << endl;
    } 

    4、函数的返回值是一个元素,返回的是一个匿名对象,所以C++编译器会调用匿名对象类的拷贝构造函数

    void display()
    {
         g();            //析构A后,匿名对象未使用,匿名对象析构。
    // Text m = g(); 匿名对象初始化一个同类型的对象,匿名对象转成有名对象,不调用匿名对象的析构函数。而是在display()函数结束析构m时调用m的析构函数
    // Text n(1,2);
    // n = g(); 匿名对象 赋值 给另外一个同类型的对象,先调用 operator=()操作符,然后匿名对象被析构。函数结束后,析构n
    } Text g() { Text A(
    1,2); //调用有参构造函数 return A; //调用拷贝构造函数,创建一个匿名对象。同时A被析构(c++调用一次析构函数)。 }

     构造函数调用规则研究:

     在定义类时,没有写构造函数,C++编译器会提供一个默认构造函数。

     写了构造函数就必须要使用它。

    浅拷贝问题:(自己编写拷贝构造函数即可:主要针对有指针成员)

    1、拷贝构造函数

    Text obj2 = obj1;       //调用obj2的拷贝构造函数  Text(Text& zty) {}

    如果Text里面有两个变量,其中一个为指针。

    char* p;

    int len;

    如果没有定义拷贝构造函数,就会调用C++自带的拷贝构造函数,这个时候:

    obj1的成员变量p和len都在栈区,p指针存放的是地址信息,指向堆中的数据(全局变量区复制过来的,注意:全局变量区和堆区都有这部分数据)

    C++自带的拷贝构造函数,执行了浅拷贝,只是在栈区把p和len复制了一份给obj2,故p也指向堆中的数据区域。在两个变量生命周期结束,需要析构时,obj2正常析构,数据析构,指针复NULL,obj1中的p就变成了野指针。

     2、等号操作符

    Text obj1("sf");

    Text obj2("safdsg");  //调用类中的有参构造函数

    obj2 = obj1;  //如果不自己重载operator=(),则调用C++自己的=操作符进行p和len简单的值拷贝,指针所指的堆中区域没有拷贝,obj1和obj2中的指针p都指向堆中相同区域的数据。

    构造函数的初始化列表:

     解决了在B类中组合了一个A类对象的(其中,A类设计了构造函数)

    根据构造函数的调用规则,设计A的构造函数,必学要用,在B中没有机会初始化A

    新的语法:

    class A
    {
    public: 
            A(int _a)
            {
              a = _a;
            }
            ~A() {}
    private:
           int a;
    }
    class B
    {
     public:
           B::B(int _b1):a2(2),a3(3)     
           {
              b1 = _b1;
           }     
    B::B(int _b1, int v2, int v3):a2(v2),a3(v3)
    {
       b1 = _b1;
    }
    private:
           int b1;
           A   a2;
           A   a3;
    }
    void main()
    {
        B zty(1);
        B lunais(1,2,3);
    }

    // 先执行被组合对象的构造函数A,按照定义顺序,而不按照初始化列表顺序。析构函数相反。

    如果类中有const对象,必须用初始化列表进行赋初值

    class B
    {
     public:
           B::B(int _b1):a2(2),a3(3),c(20)    
           {
              b1 = _b1;
           }     
    B::B(int _b1, int v2, int v3):a2(v2),a3(v3),c(20)  //c也可以赋值为其他值
    {
       b1 = _b1;
    }
    private:
           const int c;
           int b1;
           A   a2;
           A   a3;
    }

    拷贝构造函数深拷贝栗子:

    浅拷贝只是在栈区将指针内保存的地址复制了一份,而没有将堆中的数据复制一份;

    深拷贝需要申请新的内存区域,并将数据复制一份。

    Array(const Array& obj)
    {
             this->_array = new int[this->_length];
             for( int i = 0; i < _length; ++i)
             {
                   this->_array[i] = obj._array[i];    //数组   int* _array;
             }
    }
  • 相关阅读:
    java通过sort()方法实现数组和集合升序降序排列
    基本数据类型和它们的包装类
    final和static关键字
    对象的回收
    栈和堆
    成员变量和局部变量
    Nginx服务器的Websockets配置方法
    failed: Error during WebSocket handshake: Unexpected response code: 404
    使用Nginx做WebSockets代理教程
    详解Nginx反向代理WebSocket响应403的解决办法
  • 原文地址:https://www.cnblogs.com/Lunais/p/5668226.html
Copyright © 2020-2023  润新知