シリーズ: C++
cpp
254 行
· 更新日 2026-04-03
copy_control.cpp
C++/Part2_物件導向/Ch09_建構子與解構子/copy_control.cpp
// ============================================================
// Ch09 — 拷貝建構子、拷貝賦值運算子、深拷貝、Rule of Three
// 編譯:g++ -std=c++17 -Wall -o copy_control copy_control.cpp
// ============================================================
#include <iostream>
#include <algorithm>
#include <cstring>
// ============================================================
// DynamicArray — 管理原始動態陣列
// 完整實作 Rule of Three:
// 1. 解構子
// 2. 拷貝建構子
// 3. 拷貝賦值運算子
// ============================================================
class DynamicArray {
private:
int* data;
int size;
std::string label; // 用於輸出追蹤
void printAction(const std::string& action) const {
std::cout << " [" << label << "] " << action << std::endl;
}
public:
// 建構子 — 配置記憶體並初始化
DynamicArray(const std::string& label, int size, int initValue = 0)
: data(new int[size]), size(size), label(label) {
std::fill(data, data + size, initValue);
printAction("建構子:配置 " + std::to_string(size) + " 個 int");
}
// ────────────────────────────────────────
// Rule of Three (1/3):解構子
// ────────────────────────────────────────
~DynamicArray() {
printAction("解構子:釋放記憶體");
delete[] data;
}
// ────────────────────────────────────────
// Rule of Three (2/3):拷貝建構子(深拷貝)
// ────────────────────────────────────────
DynamicArray(const DynamicArray& other)
: data(new int[other.size]),
size(other.size),
label(other.label + "_copy") {
std::copy(other.data, other.data + size, data);
printAction("拷貝建構子:深拷貝 " + std::to_string(size) + " 個元素");
}
// ────────────────────────────────────────
// Rule of Three (3/3):拷貝賦值運算子
// 使用 copy-and-swap 慣用手法
// ────────────────────────────────────────
DynamicArray& operator=(const DynamicArray& other) {
printAction("拷貝賦值運算子");
// 自我賦值檢查
if (this == &other) {
printAction(" 偵測到自我賦值,跳過");
return *this;
}
// 釋放舊資源
delete[] data;
// 配置新資源並複製
size = other.size;
data = new int[size];
std::copy(other.data, other.data + size, data);
// 保留原本的 label
printAction(" 深拷貝完成");
return *this;
}
// 元素存取
int& at(int index) {
if (index < 0 || index >= size) {
throw std::out_of_range("索引超出範圍");
}
return data[index];
}
int at(int index) const {
if (index < 0 || index >= size) {
throw std::out_of_range("索引超出範圍");
}
return data[index];
}
int getSize() const { return size; }
const std::string& getLabel() const { return label; }
// 印出所有元素
void print() const {
std::cout << " [" << label << "] 大小=" << size << ", 內容=[";
for (int i = 0; i < size; ++i) {
if (i > 0) std::cout << ", ";
std::cout << data[i];
}
std::cout << "]" << std::endl;
}
// 印出內部指標位址(用於驗證深拷貝)
void printAddress() const {
std::cout << " [" << label << "] data 位址: " << data << std::endl;
}
};
// ============================================================
// 展示淺拷貝的危險
// ============================================================
class ShallowCopyDemo {
private:
int* value;
public:
ShallowCopyDemo(int v) : value(new int(v)) {
std::cout << " [ShallowDemo] 建構:*value = " << *value
<< ", 位址 = " << value << std::endl;
}
// 故意使用淺拷貝(錯誤示範!)
// 編譯器自動生成的拷貝建構子就是這樣做的
// ShallowCopyDemo(const ShallowCopyDemo& other) = default;
// 等同於:value(other.value) → 兩個物件指向同一塊記憶體!
~ShallowCopyDemo() {
std::cout << " [ShallowDemo] 解構:位址 = " << value << std::endl;
// 如果兩個物件共享同一個指標,第二次 delete 就會 crash
delete value;
}
int getValue() const { return *value; }
void setValue(int v) { *value = v; }
};
// ============================================================
// main
// ============================================================
int main() {
std::cout << "========================================" << std::endl;
std::cout << " Ch09 拷貝控制 — 深拷貝 vs 淺拷貝" << std::endl;
std::cout << "========================================\n" << std::endl;
// ------ 1. 建立與基本操作 ------
std::cout << "【1】建立 DynamicArray\n" << std::endl;
DynamicArray arr1("arr1", 5, 10);
arr1.print();
arr1.at(0) = 100;
arr1.at(2) = 200;
arr1.at(4) = 300;
std::cout << " 修改後:" << std::endl;
arr1.print();
std::cout << std::endl;
// ------ 2. 拷貝建構子(深拷貝) ------
std::cout << "【2】拷貝建構子(深拷貝)\n" << std::endl;
DynamicArray arr2(arr1); // 呼叫拷貝建構子
std::cout << std::endl;
arr1.print();
arr2.print();
std::cout << std::endl;
// 驗證是不同的記憶體位址(深拷貝)
std::cout << " 驗證深拷貝 — 記憶體位址不同:" << std::endl;
arr1.printAddress();
arr2.printAddress();
std::cout << std::endl;
// 修改 arr2 不影響 arr1
std::cout << " 修改 arr2 後:" << std::endl;
arr2.at(0) = 999;
arr1.print(); // arr1 不受影響
arr2.print();
std::cout << std::endl;
// ------ 3. 拷貝賦值運算子 ------
std::cout << "【3】拷貝賦值運算子\n" << std::endl;
DynamicArray arr3("arr3", 3, 0);
arr3.print();
std::cout << std::endl;
arr3 = arr1; // 呼叫拷貝賦值運算子
std::cout << std::endl;
arr3.print();
arr1.print();
std::cout << std::endl;
// 修改 arr3 不影響 arr1
arr3.at(0) = -1;
std::cout << " 修改 arr3 後,arr1 不受影響:" << std::endl;
arr3.print();
arr1.print();
std::cout << std::endl;
// ------ 4. 自我賦值 ------
std::cout << "【4】自我賦值測試\n" << std::endl;
{
DynamicArray& ref = arr1;
ref = arr1; // 透過參考進行自我賦值 — 應安全處理
}
arr1.print();
std::cout << std::endl;
// ------ 5. 淺拷貝的危險(概念說明) ------
std::cout << "【5】淺拷貝的危險(概念說明)\n" << std::endl;
std::cout << " 如果不實作深拷貝,以下情境會造成 double free:" << std::endl;
std::cout << std::endl;
std::cout << " DynamicArray a(\"a\", 3);" << std::endl;
std::cout << " DynamicArray b = a; // 淺拷貝 → a.data == b.data" << std::endl;
std::cout << " // b 解構 → delete[] data" << std::endl;
std::cout << " // a 解構 → delete[] data → 💥 double free!" << std::endl;
std::cout << std::endl;
std::cout << " 解決方案:實作深拷貝(配置新記憶體 + 複製內容)" << std::endl;
std::cout << " 或使用 std::vector 等自動管理記憶體的容器(Rule of Zero)" << std::endl;
std::cout << std::endl;
// ------ 6. 解構順序示範 ------
std::cout << "【6】解構順序示範\n" << std::endl;
{
std::cout << " 進入區塊:" << std::endl;
DynamicArray local1("local1", 2, 1);
DynamicArray local2("local2", 2, 2);
DynamicArray local3("local3", 2, 3);
local1.print();
local2.print();
local3.print();
std::cout << "\n 離開區塊(解構順序 = 建構的相反順序 LIFO):" << std::endl;
}
std::cout << "\n 區塊已結束,local 物件已全部銷毀" << std::endl;
std::cout << "\n========================================" << std::endl;
std::cout << " 範例結束(arr1, arr2, arr3 即將解構)" << std::endl;
std::cout << "========================================" << std::endl;
return 0;
}
関連記事
C++
c
更新日 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
記事を読む →
C++
c
更新日 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
記事を読む →
C++
cpp
更新日 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
記事を読む →
C++
c
更新日 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
記事を読む →
C++
c
更新日 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
記事を読む →
C++
cpp
更新日 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
記事を読む →