1. 程式人生 > >C++拷貝構造、賦值構造詳解

C++拷貝構造、賦值構造詳解

一、前言

寫一個用到指標的程式時,被拷貝、賦值、解構函式坑了一波,網上查相關部落格,發現關於拷貝、賦值建構函式呼叫時機大多都有錯誤,因此決定自己總結擼一發部落格。

A (A& a);                       //拷貝建構函式
A (const A& a);                 //拷貝建構函式
A& operator= (const A& a);      //賦值建構函式

先寫一個類,用作之後的示例

class A {
public:
    int* x;
    int y;
    A() = default;
    A (const
A& a) { printf ("拷貝構造\n"); this->x = a.x; this->y = a.y; } A& operator= (const A& a) { printf ("賦值構造\n"); this->x = a.x; this->y = a.y; } A (int t) { x = new int (0); y = t; printf ("address: %x, point: %x, value: %d\n"
, this, x, y); } ~A() { printf ("delete %x\n", this); } }; A f () { A ret (3); printf ("stack address: %x, point: %x, value: %d\n", &ret, ret.x, ret.y); return ret; }

二、不會呼叫拷貝建構函式和賦值建構函式的情況

1.當物件以值傳遞的方式從函式返回,且接受返回值的物件是由該返回值初始化時,不會呼叫任何建構函式。(相當於把該物件重新命名為另一個物件名)

int
main() { A c = f(); //此時不呼叫任何建構函式 printf ("global address: %x ,value: %d\n", &c, c.y); return 0; }

這裡寫圖片描述

三、拷貝建構函式

1.物件需要通過另外一個物件進行初始化

int main() {
    A a(1);
    A c = a;
    printf ("global address: %x, point: %x, value: %d\n", &c, c.x, c.y);
    return 0;
}

這裡寫圖片描述
2.物件通過值傳遞方式進入函式

void g (A ret) {
    printf ("stack address: %x, point: %x, value: %d\n", &ret, ret.x, ret.y);
}

int main() {
    A a (1);
    g (a);
    return 0;
}

這裡寫圖片描述

四、賦值建構函式

1.物件以值傳遞方式從函式返回,且接受返回值的物件已經初始化過

int main() {
    A c;
    c = f();
    printf ("global address: %x, point: %x, value: %d\n", &c, c.x, c.y);
    return 0;
}

這裡寫圖片描述
2.物件直接賦值給另一個物件,且接受值的物件已經初始化過

int main() {
    A a(1);
    A c;
    c = a;
    printf ("global address: %x, point: %x, value: %d\n", &c, c.x, c.y);
    return 0;
}

這裡寫圖片描述

五、總結

物件以值傳遞方式從函式返回時,若接受返回值的物件已經初始化過,則會呼叫賦值建構函式,且該物件還會呼叫解構函式,當物件中包含指標時,會使該指標失效,因此需要過載賦值建構函式,使用類似深拷貝或移動建構函式的方法賦值,才能避免指標失效。
物件以值傳遞方式從函式返回時,若接受返回值的物件是由該返回值初始化,則不會呼叫任何建構函式,且不會呼叫解構函式