跳转至

Effective C++基础

📖 章节简介

本章将介绍Effective C++的基础概念,包括构造/析构、资源管理和拷贝控制。

RAII与对象生命周期示意

上图串联了对象从构造到析构的完整生命周期,强调 RAII 与拷贝/移动控制的核心作用。

🏗️ 构造与析构

1. 构造函数

C++
// 构造函数
#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string name;
    int age;

public:
    // 默认构造函数
    Person() : name("匿名"), age(0) {
        cout << "默认构造函数被调用" << endl;
    }

    // 参数化构造函数
    Person(string n, int a) : name(n), age(a) {
        cout << "参数化构造函数被调用" << endl;
    }

    // 拷贝构造函数
    Person(const Person& other) : name(other.name), age(other.age) {
        cout << "拷贝构造函数被调用" << endl;
    }

    // 移动构造函数
    Person(Person&& other) : name(std::move(other.name)), age(other.age) {
        cout << "移动构造函数被调用" << endl;
    }

    // 委托构造函数
    Person(string n, int a, [[maybe_unused]] string p) : Person(n, a) {
        cout << "委托构造函数被调用" << endl;
    }

    // 析构函数
    ~Person() {
        cout << "析构函数被调用: " << name << endl;
    }

    void display() const {
        cout << "姓名: " << name << ", 年龄: " << age << endl;
    }
};

int main() {
    // 默认构造
    Person person1;
    person1.display();

    // 参数化构造
    Person person2("张三", 25);
    person2.display();

    // 拷贝构造
    Person person3 = person2;
    person3.display();

    // 移动构造
    Person person4 = std::move(person3);
    person4.display();

    return 0;
}

2. 构造函数初始化列表

C++
// 初始化列表
#include <iostream>
#include <string>
using namespace std;

class Point {
private:
    int x;
    int y;

public:
    // 初始化列表(C++11)
    Point(int x = 0, int y = 0) : x(x), y(y) {
        cout << "初始化列表: x=" << x << ", y=" << y << endl;
    }

    // 委托构造函数使用初始化列表
    Point(string pointStr) : Point() {
        // 解析字符串 "x,y"
        size_t commaPos = pointStr.find(',');
        x = stoi(pointStr.substr(0, commaPos));
        y = stoi(pointStr.substr(commaPos + 1));
    }

    void display() const {
        cout << "坐标: (" << x << ", " << y << ")" << endl;
    }
};

int main() {
    Point point1(10, 20);
    point1.display();

    Point point2("30,40");
    point2.display();

    return 0;
}

🔒 资源管理

1. RAII模式

C++
// RAII模式
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

class FileHandler {
private:
    ofstream file;

public:
    FileHandler(const string& filename) : file(filename) {
        if (!file.is_open()) {
            cout << "无法打开文件: " << filename << endl;
        }
    }

    ~FileHandler() {
        if (file.is_open()) {
            file.close();
            cout << "文件已关闭" << endl;
        }
    }

    void write(const string& content) {
        if (file.is_open()) {
            file << content;
        }
    }
};

int main() {
    {
        FileHandler handler("test.txt");
        handler.write("Hello, RAII!");
        // 文件自动在作用域结束时关闭
    }

    cout << "文件已自动关闭" << endl;

    return 0;
}

2. 智能指针

C++
// 智能指针
#include <iostream>
#include <memory>
using namespace std;

class Resource {
private:
    int* data;
    int size;

public:
    Resource(int size) : size(size) {
        data = new int[size]();
        cout << "资源分配" << endl;
    }

    ~Resource() {
        delete[] data;
        cout << "资源释放" << endl;
    }

    void display() const {
        for (int i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    // ✅ 好的做法:使用智能指针
    unique_ptr<Resource> resource = make_unique<Resource>(10);
    resource->display();

    // 资源自动释放

    // ❌ 不好的做法:手动管理内存
    Resource* rawResource = new Resource(10);
    rawResource->display();
    // delete rawResource;  // 容易忘记

    return 0;
}

📋 拷贝控制

1. 拷贝构造函数

C++
// 拷贝控制 - 深拷贝示例
#include <iostream>
#include <string>
using namespace std;

class Buffer {
private:
    int* data;
    size_t size;

public:
    // 构造函数
    Buffer(size_t s) : size(s) {
        data = new int[size];
        for (size_t i = 0; i < size; i++) {
            data[i] = 0;
        }
        cout << "构造函数" << endl;
    }

    // 深拷贝构造函数
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];
        for (size_t i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
        cout << "深拷贝构造函数" << endl;
    }

    // 深拷贝赋值运算符
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = new int[size];
            for (size_t i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
            cout << "赋值运算符(深拷贝)" << endl;
        }
        return *this;
    }

    ~Buffer() {
        delete[] data;
        cout << "析构函数" << endl;
    }

    // 访问器方法
    int& operator[](size_t index) {
        return data[index];
    }

    const int& operator[](size_t index) const {
        return data[index];
    }

    void display() const {
        for (size_t i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    Buffer buffer1(10);
    for (int i = 0; i < 10; i++) {
        buffer1[i] = i;
    }
    buffer1.display();

    // 深拷贝
    Buffer buffer2 = buffer1;
    buffer2.display();

    // 修改buffer2不会影响buffer1(深拷贝)
    buffer2[0] = 100;
    cout << "修改buffer2后:" << endl;
    cout << "buffer1: ";
    buffer1.display();
    cout << "buffer2: ";
    buffer2.display();

    return 0;
}

2. 移动语义

C++
// 移动语义
#include <iostream>
#include <utility>
using namespace std;

class BigData {
private:
    int* data;
    size_t size;

public:
    BigData(size_t s) : size(s) {
        data = new int[size];
        for (size_t i = 0; i < size; i++) {
            data[i] = i;
        }
        cout << "构造函数: 分配" << size << "个元素" << endl;
    }

    // 拷贝构造函数(深拷贝)
    BigData(const BigData& other) : size(other.size) {
        data = new int[size];
        for (size_t i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
        cout << "拷贝构造函数: 深拷贝" << endl;
    }

    // 移动构造函数
    BigData(BigData&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
        cout << "移动构造函数: 窃取资源" << endl;
    }

    // 移动赋值运算符
    BigData& operator=(BigData&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
            cout << "移动赋值运算符: 窃取资源" << endl;
        }
        return *this;
    }

    // 拷贝赋值运算符
    BigData& operator=(const BigData& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = new int[size];
            for (size_t i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
            cout << "拷贝赋值运算符: 深拷贝" << endl;
        }
        return *this;
    }

    ~BigData() {
        delete[] data;
        cout << "析构函数: 释放" << size << "个元素" << endl;
    }

    void display() const {
        for (size_t i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    BigData data1(10);
    data1.display();

    // 移动构造
    BigData data2 = std::move(data1);
    data2.display();

    // data1已经被移动,不应该再使用
    // data1.display();  // 未定义行为

    return 0;
}

💡 最佳实践

1. 遵循Rule of Five

C++
// Rule of Five
int main() {
    // ✅ 好的做法:遵循Rule of Five
    class RuleOfFive {
    public:
        RuleOfFive() = default;

        // 析构函数
        ~RuleOfFive() = default;

        // 拷贝构造函数
        RuleOfFive(const RuleOfFive&) = default;

        // 拷贝赋值运算符
        RuleOfFive& operator=(const RuleOfFive&) = default;

        // 移动构造函数
        RuleOfFive(RuleOfFive&&) noexcept = default;

        // 移动赋值运算符
        RuleOfFive& operator=(RuleOfFive&&) noexcept = default;
    };

    // ❌ 不好的做法:不遵循Rule of Five
    class BadClass {
    public:
        BadClass() = default;

        ~BadClass() = default;

        // 没有拷贝构造函数
        // 拷贝赋值运算符可能有问题

        // 没有移动构造函数
        // 移动赋值运算符可能有问题
    };

    return 0;
}

2. 资源管理

C++
// 资源管理
int main() {
    // ✅ 好的做法:使用RAII
    class GoodClass {
    private:
        int* data;

    public:
        GoodClass(int size) : data(new int[size]) {}

        ~GoodClass() {
            delete[] data;
            cout << "资源自动释放" << endl;
        }
    };

    // ❌ 不好的做法:不使用RAII
    class BadClass {
    private:
        int* data;

    public:
        BadClass(int size) {
            data = new int[size];
        }

        // 忘记释放内存
        ~BadClass() {
            // delete[] data;  // 容易忘记
        }
    };

    return 0;
}

📝 练习题

基础题

  1. 什么是RAII模式?
  2. 拷贝和移动有什么区别?
  3. Rule of Three是什么?

进阶题

  1. 实现一个资源管理类。
  2. 正确实现拷贝语义。
  3. 使用智能指针管理资源。

实践题

  1. 创建一个文件处理类。
  2. 实现一个智能指针包装器。
  3. 构建一个资源池。

📚 推荐阅读

🔗 下一章

Effective C++进阶 - 学习Effective C++的进阶内容。