# Way to C++: 1. Copy Constructor

# 前言

這是這幾天的c++學習歷程的的整理，主要在於如何理解 c++11 的資源管理。

這是極為重要的，在 c ，資源的釋放都需要小心翼翼地寫:

```c
Obj ReadObjFromFile(const char* filename) {  
    FILE* fp = fopen(filename, "r"); //簡化，沒有判斷 fopen 是否成功  
    Obj obj = ReadObjFromFp(fp);  
    fclose(fp);  
    return obj;  
}
```

在 `return` 的地方都必須釋放之前處理的資源。這樣的手動管理資源算是 c 容易被**討厭**的一點。

而在 c++ 裡面，利用了 class 的 destructor 會在物件被銷毀時呼叫，我們可以做到這件事情：

```cpp
Obj ReadObjFromFile(const char* filename) {  
    File fp(filename);   
    return ReadObj(fp); // 先忽略 fp 複製問題  
}
```

在離開 `ReadObjFromFile` 時， fp 自然銷毀。

# 真的，那麼好寫?

事實上，我有很長一段時間都以為做到這件事情只需要 constructor 和 destructor，完全沒有注意到另外四個函數:

* copy constructor
    
* copy assignment
    
* move constructor (c++11 開始有)
    
* move assignment (c++11 開始有)
    

簡單來說，在一個 class 成員沒有指標時，這些基本上不太需要去注意，因為編譯器會自動生成合適的函數(一些人稱做 swallow copy，不過應該不太合適，因為假如 class 成員有一個好好定義的 class ，像是 `std::vector`，那 vector 裡面的東西也會按照 **copy** 的語意進行複製之類)。但是在有指標時，就會出問題。

舉個例子，以一個\*\*簡單(?)\*\*的 integer buffer 為例子，只寫了 constructor 和 destructor：

```cpp
#include <cstdio>

class IntBuff {
public:
    IntBuff() = default;
    explicit IntBuff(size_t sz) : sz(sz) {
        if (sz) {
            arr = new int[sz];
        }
    };

    ~IntBuff() {
        printf("%p\n", arr);
        delete[] arr;
    }

private:
    int* arr = nullptr;
    size_t sz = 0;
};

int main() {
    {
        IntBuff a1(size_t(10)), b1;
        b1 = a1;
    }
    return 0;
}
```

可以發現，只要簡單的 `b1 = a1` ，便複製了指標，並且讓指標被刪除兩次。但複製行為**應該**要是複製弄一個新的指標，並且把來源指標的內容全部複製過去。

# 甚麼是複製，怎麼處理?

在不考慮 r-value reference 的情況下，複製可以在初始化時或指定時觸發：

```cpp
Object obj2("a");  
Object obj1(obj2); // 初始化時給另外一個 obj  
obj1 = obj2; // 或者指定
```

要讓這個行為在 `IntBuff` 上是好的，就需要好好定義這兩個函數：

```cpp
IntBuff::IntBuff(const IntBuff& ib); // copy constructor  
IntBuff& IntBuff::operator=(const IntBuff& ib); // copy assignment
```

```cpp
#include <algorithm>
#include <cstdio>
#include <memory>

class IntBuff {
public:
    IntBuff() = default;
    explicit IntBuff(size_t sz) : sz(sz) {
        if (sz) {
            arr = new int[sz];
        }
    };

    IntBuff(const IntBuff& ib) {
        sz = ib.sz;
        arr = new int[sz];
        for (int lx = 0; lx < sz; lx++) {
            arr[lx] = ib.arr[lx];
        }
    }

    IntBuff& operator=(const IntBuff& ib) {
        IntBuff tmp(ib);  // copy-and-swap
        std::swap(tmp.arr, arr);
        std::swap(tmp.sz, sz);
        return *this;
    }

    ~IntBuff() {
        printf("%p\n", arr);
        delete[] arr;
    }

private:
    int* arr = nullptr;
    size_t sz = 0;
};

int main() {
    {
        IntBuff a1(size_t(10)), b1;
        b1 = a1;
    }
    return 0;
}
```

# 這樣就好了?

不是，這篇也只講到 **copy** 這個 c++11之前就有的東西。而假如只有 **copy** 這個行為，會一個問題：就是效能，而這會需要用到 **move** 來解決。

然後是 **copy-and-swap**，眼尖的人會發現，在 copy assignment 函數，先觸發了 copy constructor ，然後再交換成員，比較完整的原因會在之後解釋。
