構造函數(三) 拷貝構造函數

什麼是拷貝構造函數

拷貝聽起來真高級
拷貝構造函數形如c++

class_name(const class_name &object_name)

拷貝構造函數是一種特殊的構造函數,只有一個參數,這個參數是本類中的一個對象,以引用的形式傳參,通常用const修飾,使參數值不變。函數

若是沒有定義拷貝構造函數,編譯器會自動隱式生成一個拷貝構造函數,用來簡單的複製類中每一個成員變量。this

一個簡單的類對象拷貝spa

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int a, b;
    public :
        A(int x, int y) : a(x), b(y) {}
        void print() {
            printf("%d %d\n", a, b);
        }
};

int main() {
    A a(10, 20);
    A b = a;
    b.print();
    return 0;
}

輸出指針

10 20code

具體看一下拷貝構造函數怎麼用。對象

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int a, b;
    public :
        A(int x, int y) : a(x), b(y) {}
        A(const A &c) {
            a = c.a;
            b = c.b;
        }
        void print() {
            printf("%d %d\n", a, b);
        }
};

int main() {
    A a(10, 20);
    A b = a;
    b.print();
    return 0;
}

這樣手動的寫如何複製blog

爲何要引用

爲何用引用的方式傳參呢。
舉個反例遞歸

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int a;
    public :
        A(int x) : a(x) {}
        A(const A c) {
            a = c.a;
        }
        void print() {
            printf("%d\n", a);
        }
};

int main() {
    A a(10);
    A b = a;
    b.print();
    return 0;
}

A b = a時,調用了傳值型的拷貝構造函數,
至關於b.A(a)
由於是傳的值,因此要用a的值建立一個副本對象c,讓c的值爲a,則又須要調用構造函數c.A(a),這樣會無限創造副本對象c,無限調用c.A(a);
因此
引用是爲了防止無限遞歸。
其實沒有加引用的話編譯器也是不過編譯的。內存

拷貝構造函數的調用時機

1. 對象須要經過另外一個對象對其進行賦值

A a(10, 20);
A b = a;

2. 當函數的參數是類的對象

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int a, b;
    public :
        A(int x, int y) : a(x), b(y) {}
        A(const A &c) {a = c.a, b = c.b;}
        void print() {printf("%d\n", a * b);}
};

void fun(A c) {     //函數的形參是類的對象 
    c.print();
}

int main() {
    A a(10, 20);
    fun(a);         //實參是類的對象,調用函數時將複製一個新對象c
    return 0;
}

3. 當函數的返回值是類的對象

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int a, b;
    public :
        A(int x, int y) : a(x), b(y) {}
        A(const A &c) {a = c.a, b = c.b;}
        void print() {printf("%d\n", a * b);}
};

A fun() {   
    A b(20, 10);
    return b;       //函數的返回值是類的對象 
}

int main() {
    A a = fun();    //fun()返回 A 類的臨時對象,並賦值給a 
    a.print();
    return 0;
}

深拷貝和淺拷貝

默認拷貝構造函數能夠完成對象的數據值得簡單複製,這就是淺拷貝。

對象的成員是指向堆時,淺拷貝只是將指針簡單複製一遍,指向了原有的內存,而深拷貝是將新建的指針指向了一塊新的內存。

淺拷貝

淺拷貝就是將數據簡單複製一下,就像上面講的。

看這段代碼

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int *p;
    public :
        A(int x) {
            p = new int(x);
            printf("Constructor\n");
        }
        A(const A &oth) {
            this->p = new int(*oth.p);
            printf("Constructor\n");
        }
        ~A() {
            if (p != NULL) delete p;
            printf("Destructor\n"); 
        }
        void print() { printf("%d\n", *p); }
        
};
int main() {
    A a(10);
    A b = a;
    return 0;
}

這是段代碼出錯了,爲何?

如圖所示,
未命名.PNG

b把a的指針簡單複製了一遍,二者都指向了同一塊內存,釋放內存的時候被釋放了兩次,致使出錯。

深拷貝

對於上面的代碼,咱們手寫一個拷貝構造函數,像這樣

#include <bits/stdc++.h>
using namespace std;
class A {
    private :
        int *p;
    public :
        A(int x) {
            p = new int(x);
            printf("Constructor\n");
        }
        A(const A &oth) {
            this->p = new int(*oth.p);
            printf("Constructor\n");
        }
        ~A() {
            if (p != NULL) delete p;
            printf("Destructor\n"); 
        }
        void print() { printf("%d\n", *p); }
        
};
int main() {
    A a(10);
    A b = a;
    return 0;
}

咱們給b初始化時,給它新開闢了一段內存空間
未命名.PNG 這樣,深拷貝就沒有上述淺拷貝時內存釋放兩次的狀況了。

相關文章
相關標籤/搜索