S SmartDocs
Serie: C++ cpp 267 righe · Aggiornato 2026-04-03

constructors.cpp

C++/Part2_物件導向/Ch09_建構子與解構子/constructors.cpp

// ============================================================
// Ch09 — 建構子完整範例
//   預設、帶參數、多載、初始化列表、委託建構子、
//   explicit、= default / = delete
// 編譯:g++ -std=c++17 -Wall -o constructors constructors.cpp
// ============================================================

#include <iostream>
#include <string>

// ============================================================
// 1. Date — 多種建構子示範
// ============================================================

class Date {
private:
    int year;
    int month;
    int day;

public:
    // 完整參數建構子
    Date(int year, int month, int day)
        : year(year), month(month), day(day) {
        std::cout << "    Date(" << year << ", " << month << ", "
                  << day << ") 完整建構子" << std::endl;
    }

    // 委託建構子 — 預設日期
    Date() : Date(2000, 1, 1) {
        std::cout << "    → 委託自預設建構子" << std::endl;
    }

    // 委託建構子 — 只給年份
    Date(int year) : Date(year, 1, 1) {
        std::cout << "    → 委託自單參數建構子" << std::endl;
    }

    // const 成員函式
    int getYear()  const { return year; }
    int getMonth() const { return month; }
    int getDay()   const { return day; }

    bool isLeapYear() const {
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }

    void print() const {
        std::cout << "    " << year << "/" << month << "/" << day;
        if (isLeapYear()) std::cout << "(閏年)";
        std::cout << std::endl;
    }
};

// ============================================================
// 2. Person — 初始化列表與 const 成員
// ============================================================

class Person {
private:
    const int id;       // const 成員 → 必須用初始化列表
    std::string name;
    int age;

public:
    // 必須使用初始化列表初始化 const 成員
    Person(int id, const std::string& name, int age)
        : id(id), name(name), age(age) {}

    // 委託建構子
    Person(int id, const std::string& name)
        : Person(id, name, 0) {}

    void print() const {
        std::cout << "    [ID:" << id << "] " << name
                  << ", " << age << " 歲" << std::endl;
    }

    int getId() const { return id; }
};

// ============================================================
// 3. Meter — explicit 關鍵字示範
// ============================================================

class Meter {
private:
    double value;

public:
    // 加上 explicit 防止隱式轉換
    explicit Meter(double v) : value(v) {}

    double getValue() const { return value; }

    void print() const {
        std::cout << "    " << value << " 公尺" << std::endl;
    }
};

// 接受 Meter 的函式 — 用於測試隱式轉換
void printDistance(const Meter& m) {
    std::cout << "    距離: " << m.getValue() << " m" << std::endl;
}

// ============================================================
// 4. Counter — = default 與 = delete
// ============================================================

class Counter {
private:
    int count;

public:
    Counter() = default;                          // 明確要求預設建構子

    explicit Counter(int initial) : count(initial) {}

    Counter(const Counter&) = delete;             // 禁止拷貝
    Counter& operator=(const Counter&) = delete;  // 禁止拷貝賦值

    void increment() { ++count; }
    void decrement() { --count; }
    int getCount() const { return count; }

    void print() const {
        std::cout << "    Counter = " << count << std::endl;
    }
};

// ============================================================
// 5. InitOrder — 初始化順序示範
// ============================================================

class InitOrder {
private:
    int a;  // 先宣告 → 先初始化
    int b;  // 後宣告 → 後初始化

public:
    // 初始化列表的順序應與宣告順序一致
    // 如果寫成 b(val*2), a(val),編譯器會警告
    InitOrder(int val) : a(val), b(a + 1) {
        std::cout << "    a = " << a << ", b = " << b << std::endl;
        std::cout << "    (初始化順序依宣告順序:先 a 再 b)" << std::endl;
        std::cout << "    (b 依賴 a 的值,因為 a 宣告在前所以安全)" << std::endl;
    }
};

// ============================================================
// 6. Logger — 解構子示範
// ============================================================

class Logger {
private:
    std::string name;

public:
    Logger(const std::string& name) : name(name) {
        std::cout << "    [Logger] " << name << " 建構" << std::endl;
    }

    ~Logger() {
        std::cout << "    [Logger] " << name << " 解構" << std::endl;
    }

    void log(const std::string& msg) const {
        std::cout << "    [" << name << "] " << msg << std::endl;
    }
};

// ============================================================
// main
// ============================================================

int main() {
    std::cout << "========================================" << std::endl;
    std::cout << " Ch09 建構子完整範例" << std::endl;
    std::cout << "========================================\n" << std::endl;

    // ------ 1. Date — 多種建構子 ------
    std::cout << "【1】Date — 建構子多載與委託\n" << std::endl;

    Date d1;               // 預設建構子 → 委託 → Date(2000,1,1)
    std::cout << std::endl;
    Date d2(2024);         // 單參數 → 委託 → Date(2024,1,1)
    std::cout << std::endl;
    Date d3(2024, 3, 15);  // 完整建構子
    std::cout << std::endl;

    std::cout << "  建立的日期:" << std::endl;
    d1.print();
    d2.print();
    d3.print();
    std::cout << std::endl;

    // ------ 2. Person — const 成員與初始化列表 ------
    std::cout << "【2】Person — const 成員必須用初始化列表\n" << std::endl;

    Person p1(1001, "小明", 20);
    Person p2(1002, "小華", 22);
    Person p3(1003, "小美");     // 委託建構子,age 預設 0

    p1.print();
    p2.print();
    p3.print();

    // p1.id = 999;  // 編譯錯誤!const 成員不可修改
    std::cout << std::endl;

    // ------ 3. Meter — explicit 示範 ------
    std::cout << "【3】Meter — explicit 防止隱式轉換\n" << std::endl;

    Meter m1(100.0);          // ✓ 直接初始化
    Meter m2{50.5};           // ✓ 大括號初始化
    // Meter m3 = 30.0;       // ✗ 編譯錯誤!explicit 禁止隱式轉換

    m1.print();
    m2.print();

    printDistance(Meter(200.0));   // ✓ 顯式建構
    // printDistance(200.0);       // ✗ 編譯錯誤!不允許隱式轉換

    std::cout << std::endl;

    // ------ 4. Counter — = default / = delete ------
    std::cout << "【4】Counter — = default / = delete\n" << std::endl;

    Counter c1(10);
    c1.increment();
    c1.increment();
    c1.print();

    // Counter c2 = c1;              // ✗ 編譯錯誤!拷貝被 delete
    // Counter c3; c3 = c1;          // ✗ 編譯錯誤!拷貝賦值被 delete

    std::cout << "  拷貝建構子與拷貝賦值運算子已被 delete" << std::endl;
    std::cout << std::endl;

    // ------ 5. InitOrder — 初始化順序 ------
    std::cout << "【5】初始化順序(依宣告順序,非初始化列表順序)\n" << std::endl;

    InitOrder io(5);
    std::cout << std::endl;

    // ------ 6. Logger — 解構子示範 ------
    std::cout << "【6】解構子自動呼叫示範\n" << std::endl;

    {
        std::cout << "  進入內部區塊" << std::endl;
        Logger log1("A");
        Logger log2("B");
        log1.log("正在工作...");
        log2.log("也在工作...");
        std::cout << "  即將離開區塊" << std::endl;
    }
    // log1 和 log2 離開作用域,解構子自動被呼叫
    // 注意:解構順序是建構順序的相反(LIFO)

    std::cout << "\n  已離開區塊,物件已被銷毀" << std::endl;

    std::cout << "\n========================================" << std::endl;
    std::cout << " 範例結束" << std::endl;
    std::cout << "========================================" << std::endl;

    return 0;
}

Articoli correlati