ITPub博客

首页 > 应用开发 > IT综合 > C++类与对象深度剖析:6个默认成员函数详解

C++类与对象深度剖析:6个默认成员函数详解

投稿 IT综合 作者:bhluo 时间:2025-12-31 15:44:02 0 删除 编辑

# C++类与对象深度剖析:6个默认成员函数详解


## 默认构造函数:对象的初始化起点


构造函数是对象创建的起点,编译器会在需要时自动生成默认构造函数。理解构造函数的各种形式对于编写健壮的C++代码至关重要。


### 默认构造函数的基础形式


```cpp

class Person {

private:

    std::string name;

    int age;

    double height;

    

public:

    // 1. 默认构造函数

    Person() : name("Unknown"), age(0), height(0.0) {

        std::cout << "默认构造函数被调用" << std::endl;

    }

    

    // 2. 带参数的构造函数

    Person(const std::string& n, int a, double h) 

        : name(n), age(a), height(h) {

        std::cout << "带参数构造函数被调用: " << name << std::endl;

    }

    

    // 3. 委托构造函数(C++11)

    Person(const std::string& n) : Person(n, 0, 0.0) {

        std::cout << "委托构造函数被调用" << std::endl;

    }

    

    // 4. 显式默认构造函数

    Person() = default;

    

    // 5. 显示删除构造函数

    Person(const Person&) = delete;  // 禁止拷贝构造

};


void constructor_demo() {

    std::cout << "=== 构造函数演示 ===" << std::endl;

    

    // 调用默认构造函数

    Person p1;

    

    // 调用带参数构造函数

    Person p2("Alice", 25, 165.5);

    

    // 调用委托构造函数

    Person p3("Bob");

    

    // 使用列表初始化(C++11)

    Person p4{"Charlie", 30, 175.0};

    

    // 动态分配

    Person* p5 = new Person("David", 35, 180.0);

    delete p5;

    

    // 数组初始化

    Person people[3] = {

        Person("Eve", 28, 160.0),

        Person("Frank", 32, 172.0),

        Person()  // 默认构造

    };

}

```


### 成员初始化列表的重要性


```cpp

class ComplexClass {

private:

    const int id;           // const成员必须在初始化列表初始化

    int& ref;               // 引用成员必须在初始化列表初始化

    std::vector<int> data;  // 非内置类型应在初始化列表初始化

    

public:

    // 正确的构造函数:使用初始化列表

    ComplexClass(int id_value, int& ref_value, size_t size) 

        : id(id_value), ref(ref_value), data(size) {

        // id = id_value;     // 错误:const成员不能赋值

        // ref = ref_value;   // 错误:引用必须在初始化时绑定

        // data.resize(size); // 可以,但效率低于初始化列表

    }

    

    // 错误的构造函数:在函数体内初始化

    ComplexClass(int id_value, int& ref_value) 

        : id(id_value), ref(ref_value) {  // 前两个成员正确初始化

        data = std::vector<int>(100);     // 先默认构造,再赋值

    }

    

    // 成员初始化顺序问题

    class InitializationOrder {

        int a;

        int b;

    public:

        // 注意:初始化顺序只与成员声明顺序有关,与初始化列表顺序无关

        InitializationOrder() : b(10), a(b + 1) {

            // 危险:a初始化为 b+1,但b尚未初始化!

            // a的值是未定义的

        }

    };

};


void initialization_list_demo() {

    int external_value = 42;

    ComplexClass obj(1, external_value, 10);

    

    std::cout << "初始化列表演示完成" << std::endl;

}

```


## 析构函数:资源释放的守护者


析构函数在对象生命周期结束时自动调用,负责资源清理工作。


```cpp

class ResourceHolder {

private:

    int* dynamic_array;

    size_t size;

    FILE* file_handle;

    

public:

    // 构造函数

    ResourceHolder(size_t arr_size, const char* filename) 

        : size(arr_size), file_handle(nullptr) {

        

        // 动态分配数组

        dynamic_array = new int[size];

        std::cout << "分配了 " << size << " 个整数的数组" << std::endl;

        

        // 打开文件

        file_handle = fopen(filename, "w");

        if (file_handle) {

            std::cout << "打开了文件: " << filename << std::endl;

        }

    }

    

    // 析构函数

    ~ResourceHolder() {

        std::cout << "析构函数被调用" << std::endl;

        

        // 释放动态数组

        delete[] dynamic_array;

        std::cout << "释放了动态数组" << std::endl;

        

        // 关闭文件

        if (file_handle) {

            fclose(file_handle);

            std::cout << "关闭了文件" << std::endl;

        }

    }

    

    // 禁止拷贝(防止浅拷贝问题)

    ResourceHolder(const ResourceHolder&) = delete;

    ResourceHolder& operator=(const ResourceHolder&) = delete;

};


void destructor_demo() {

    std::cout << "=== 析构函数演示 ===" << std::endl;

    

    {

        std::cout << "进入内部作用域" << std::endl;

        ResourceHolder holder1(100, "test.txt");

        

        // 动态分配对象

        ResourceHolder* holder2 = new ResourceHolder(50, "temp.txt");

        delete holder2;  // 显式调用析构函数

        

        std::cout << "离开内部作用域" << std::endl;

        // holder1的析构函数在这里自动调用

    }

    

    std::cout << "回到外部作用域" << std::endl;

    

    // 异常安全演示

    try {

        ResourceHolder holder3(200, "data.txt");

        throw std::runtime_error("测试异常");

        // holder3的析构函数在异常抛出后仍会被调用

    } catch (const std::exception& e) {

        std::cout << "捕获异常: " << e.what() << std::endl;

    }

}

```


## 拷贝构造函数与拷贝赋值运算符


### 拷贝构造函数


```cpp

class String {

private:

    char* data;

    size_t length;

    

    // 辅助函数:深拷贝实现

    void deep_copy(const String& other) {

        length = other.length;

        data = new char[length + 1];

        std::copy(other.data, other.data + length + 1, data);

    }

    

public:

    // 构造函数

    String(const char* str = "") : length(0), data(nullptr) {

        if (str) {

            length = std::strlen(str);

            data = new char[length + 1];

            std::strcpy(data, str);

        }

    }

    

    // 拷贝构造函数(深拷贝)

    String(const String& other) : length(0), data(nullptr) {

        std::cout << "拷贝构造函数被调用" << std::endl;

        deep_copy(other);

    }

    

    // 移动构造函数(C++11)

    String(String&& other) noexcept 

        : length(other.length), data(other.data) {

        std::cout << "移动构造函数被调用" << std::endl;

        other.length = 0;

        other.data = nullptr;

    }

    

    // 析构函数

    ~String() {

        delete[] data;

    }

    

    // 拷贝赋值运算符

    String& operator=(const String& other) {

        std::cout << "拷贝赋值运算符被调用" << std::endl;

        

        if (this != &other) {

            // 释放现有资源

            delete[] data;

            

            // 深拷贝新资源

            deep_copy(other);

        }

        return *this;

    }

    

    // 移动赋值运算符(C++11)

    String& operator=(String&& other) noexcept {

        std::cout << "移动赋值运算符被调用" << std::endl;

        

        if (this != &other) {

            delete[] data;

            

            length = other.length;

            data = other.data;

            

            other.length = 0;

            other.data = nullptr;

        }

        return *this;

    }

    

    // 显示内容

    void display() const {

        if (data) {

            std::cout << data;

        }

    }

};


void copy_constructor_demo() {

    std::cout << "=== 拷贝构造函数演示 ===" << std::endl;

    

    // 1. 直接初始化(调用拷贝构造)

    String s1("Hello");

    String s2 = s1;  // 拷贝构造函数

    

    // 2. 函数参数传递(调用拷贝构造)

    auto print_string = [](String str) {

        str.display();

        std::cout << std::endl;

    };

    print_string(s1);  // 拷贝构造函数

    

    // 3. 函数返回值优化(可能调用拷贝构造或移动构造)

    auto create_string = []() -> String {

        String temp("World");

        return temp;  // 可能调用移动构造(C++11及以上)

    };

    String s3 = create_string();

    

    // 4. 容器操作(调用拷贝构造)

    std::vector<String> strings;

    strings.push_back(s1);       // C++11前:拷贝构造

    strings.emplace_back("C++"); // C++11:直接构造

    

    // 5. 拷贝赋值运算符

    String s4("Original");

    s4 = s1;  // 拷贝赋值运算符

}

```


### 拷贝赋值运算符的现代实现


```cpp

class ModernString {

private:

    char* data;

    size_t length;

    

public:

    ModernString(const char* str = "") : length(0), data(nullptr) {

        if (str) {

            length = std::strlen(str);

            data = new char[length + 1];

            std::strcpy(data, str);

        }

    }

    

    // 拷贝赋值运算符的现代实现(拷贝交换惯用法)

    ModernString& operator=(ModernString other) {  // 注意:按值传递

        std::cout << "拷贝赋值运算符(拷贝交换)" << std::endl;

        swap(*this, other);

        return *this;

    }

    

    // 移动赋值运算符

    ModernString& operator=(ModernString&& other) noexcept {

        std::cout << "移动赋值运算符" << std::endl;

        if (this != &other) {

            delete[] data;

            length = other.length;

            data = other.data;

            other.length = 0;

            other.data = nullptr;

        }

        return *this;

    }

    

    // 友元swap函数

    friend void swap(ModernString& first, ModernString& second) noexcept {

        using std::swap;

        swap(first.length, second.length);

        swap(first.data, second.data);

    }

    

    ~ModernString() {

        delete[] data;

    }

};

```


## 移动构造函数与移动赋值运算符(C++11)


移动语义允许资源所有权的转移,避免不必要的深拷贝。


```cpp

class Buffer {

private:

    int* data;

    size_t size;

    

public:

    // 构造函数

    Buffer(size_t s) : size(s), data(new int[s]) {

        std::cout << "构造Buffer,大小: " << size << std::endl;

    }

    

    // 拷贝构造函数

    Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {

        std::cout << "拷贝构造Buffer" << std::endl;

        std::copy(other.data, other.data + size, data);

    }

    

    // 移动构造函数(noexcept很重要)

    Buffer(Buffer&& other) noexcept 

        : size(other.size), data(other.data) {

        std::cout << "移动构造Buffer" << std::endl;

        other.size = 0;

        other.data = nullptr;

<"api.q6k0.org.cn"><"bs.q6k0.org.cn"><"tv.q6k0.org.cn">

    }

    

    // 拷贝赋值运算符

    Buffer& operator=(const Buffer& other) {

        std::cout << "拷贝赋值Buffer" << std::endl;

        if (this != &other) {

            delete[] data;

            size = other.size;

            data = new int[size];

            std::copy(other.data, other.data + size, data);

        }

        return *this;

    }

    

    // 移动赋值运算符

    Buffer& operator=(Buffer&& other) noexcept {

        std::cout << "移动赋值Buffer" << std::endl;

        if (this != &other) {

            delete[] data;

            size = other.size;

            data = other.data;

            other.size = 0;

            other.data = nullptr;

        }

        return *this;

    }

    

    // 析构函数

    ~Buffer() {

        std::cout << "销毁Buffer,大小: " << size << std::endl;

        delete[] data;

    }

    

    // 模拟资源密集型操作

    void fill_with_random() {

        for (size_t i = 0; i < size; ++i) {

            data[i] = rand() % 100;

        }

    }

};


void move_semantics_demo() {

    std::cout << "=== 移动语义演示 ===" << std::endl;

    

    // 创建大对象

    Buffer buffer1(1000000);

    buffer1.fill_with_random();

    

    // 移动构造演示

    Buffer buffer2 = std::move(buffer1);  // 移动构造

    std::cout << "移动后,buffer1大小: " << buffer1.size << std::endl;

    

    // 移动赋值演示

    Buffer buffer3(500000);

    buffer3 = std::move(buffer2);  // 移动赋值

    

    // 容器中的移动语义

    std::vector<Buffer> buffers;

    buffers.reserve(3);

    

    // push_back的移动优化

    Buffer temp1(100);

    buffers.push_back(std::move(temp1));  // 移动构造

    

    // emplace_back直接构造

    buffers.emplace_back(200);

    

    // 返回值优化 + 移动语义

    auto create_buffer = []() -> Buffer {

        Buffer local(300);

        local.fill_with_random();

        return local;  // 可能触发NRVO或移动构造

    };

    

    Buffer buffer4 = create_buffer();

    

    // std::unique_ptr演示移动语义

    std::unique_ptr<Buffer> ptr1 = std::make_unique<Buffer>(400);

    std::unique_ptr<Buffer> ptr2 = std::move(ptr1);  // 所有权转移

    

    std::cout << "移动语义演示完成" << std::endl;

}

```


## 默认成员函数的生成规则


### 特殊成员函数的生成条件


```cpp

class RuleDemo {

private:

    int value;

    std::string name;

    

public:

    // 情况1:如果用户声明了任何构造函数,编译器不再生成默认构造函数

    RuleDemo(int v) : value(v) {}

    // RuleDemo() = default;  // 需要显式声明

    

    // 情况2:如果用户声明了拷贝构造函数,编译器不再生成移动构造函数

    RuleDemo(const RuleDemo& other) : value(other.value), name(other.name) {

        std::cout << "用户定义拷贝构造" << std::endl;

    }

    

    // 情况3:如果用户声明了拷贝赋值运算符,编译器不再生成移动赋值运算符

    RuleDemo& operator=(const RuleDemo& other) {

        if (this != &other) {

            value = other.value;

            name = other.name;

        }

        std::cout << "用户定义拷贝赋值" << std::endl;

        return *this;

    }

    

    // 情况4:如果用户声明了析构函数,编译器仍会生成拷贝操作,但C++11后不推荐

    ~RuleDemo() {

        std::cout << "用户定义析构函数" << std::endl;

    }

    

    // 情况5:如果用户声明了移动构造函数或移动赋值运算符,编译器会删除拷贝操作

    // RuleDemo(RuleDemo&&) = delete;  // 这会导致拷贝操作被删除

};


class ModernRule {

private:

    std::unique_ptr<int> resource;  // 不可拷贝的成员

    

public:

    // 用户声明了移动构造函数

    ModernRule(ModernRule&& other) noexcept = default;

    

    // 结果:拷贝构造函数被隐式删除

    // ModernRule(const ModernRule&) = delete;

    

    // 用户声明了移动赋值运算符

    ModernRule& operator=(ModernRule&& other) noexcept = default;

    

    // 结果:拷贝赋值运算符被隐式删除

    // ModernRule& operator=(const ModernRule&) = delete;

};


void generation_rules_demo() {

    std::cout << "=== 默认函数生成规则演示 ===" << std::endl;

    

    RuleDemo obj1(10);

    // RuleDemo obj2;  // 错误:没有默认构造函数

    

    RuleDemo obj3(obj1);  // 调用用户定义的拷贝构造

    RuleDemo obj4 = obj1; // 调用用户定义的拷贝构造

    

    // 测试移动语义

    ModernRule m1;

    // ModernRule m2 = m1;  // 错误:拷贝构造被删除

    ModernRule m3 = std::move(m1);  // 正确:移动构造可用

}

```


## 综合示例:实现一个完整的类


```cpp

class Matrix {

private:

    double** data;

    size_t rows;

    size_t cols;

    

    // 辅助函数:分配内存

    void allocate_memory(size_t r, size_t c) {

        rows = r;

        cols = c;

        data = new double*[rows];

        for (size_t i = 0; i < rows; ++i) {

            data[i] = new double[cols]();  // 值初始化为0

        }

    }

    

    // 辅助函数:释放内存

    void free_memory() noexcept {

        if (data) {

            for (size_t i = 0; i < rows; ++i) {

                delete[] data[i];

            }

            delete[] data;

            data = nullptr;

        }

        rows = cols = 0;

    }

    

    // 辅助函数:深拷贝

    void deep_copy(const Matrix& other) {

        allocate_memory(other.rows, other.cols);

        for (size_t i = 0; i < rows; ++i) {

            std::copy(other.data[i], other.data[i] + cols, data[i]);

        }

    }

    

public:

    // 1. 默认构造函数

    Matrix() : data(nullptr), rows(0), cols(0) {

        std::cout << "默认构造Matrix" << std::endl;

    }

    

    // 2. 带参数的构造函数

    Matrix(size_t r, size_t c) : data(nullptr), rows(0), cols(0) {

        std::cout << "构造 " << r << "x" << c << " Matrix" << std::endl;

        allocate_memory(r, c);

    }

    

    // 3. 拷贝构造函数

    Matrix(const Matrix& other) : data(nullptr), rows(0), cols(0) {

        std::cout << "拷贝构造Matrix" << std::endl;

        deep_copy(other);

    }

    

    // 4. 移动构造函数(noexcept很重要)

    Matrix(Matrix&& other) noexcept 

        : data(other.data), rows(other.rows), cols(other.cols) {

        std::cout << "移动构造Matrix" << std::endl;

        other.data = nullptr;

        other.rows = other.cols = 0;

    }

    

    // 5. 拷贝赋值运算符(异常安全)

    Matrix& operator=(const Matrix& other) {

        std::cout << "拷贝赋值Matrix" << std::endl;

        if (this != &other) {

            // 拷贝交换惯用法

            Matrix temp(other);  // 可能抛出异常

            swap(*this, temp);   // noexcept操作

        }

        return *this;

    }

    

    // 6. 移动赋值运算符

    Matrix& operator=(Matrix&& other) noexcept {

        std::cout << "移动赋值Matrix" << std::endl;

        if (this != &other) {

            free_memory();  // noexcept

            data = other.data;

            rows = other.rows;

            cols = other.cols;

            other.data = nullptr;

            other.rows = other.cols = 0;

        }

        return *this;

    }

    

    // 7. 析构函数

    ~Matrix() noexcept {

        std::cout << "销毁Matrix " << rows << "x" << cols << std::endl;

<"cba.q6k0.org.cn"><"sjXqr.q6k0.org.cn"><"read.q6k0.org.cn">

        free_memory();

    }

    

    // 友元swap函数

    friend void swap(Matrix& first, Matrix& second) noexcept {

        using std::swap;

        swap(first.data, second.data);

        swap(first.rows, second.rows);

        swap(first.cols, second.cols);

    }

    

    // 访问元素(const和非const版本)

    double& at(size_t i, size_t j) {

        if (i >= rows || j >= cols) {

            throw std::out_of_range("Matrix索引越界");

        }

        return data[i][j];

    }

    

    const double& at(size_t i, size_t j) const {

        if (i >= rows || j >= cols) {

            throw std::out_of_range("Matrix索引越界");

        }

        return data[i][j];

    }

    

    // 获取维度

    size_t getRows() const { return rows; }

    size_t getCols() const { return cols; }

    

    // 矩阵加法

    Matrix operator+(const Matrix& other) const {

        if (rows != other.rows || cols != other.cols) {

            throw std::invalid_argument("矩阵维度不匹配");

        }

        

        Matrix result(rows, cols);

        for (size_t i = 0; i < rows; ++i) {

            for (size_t j = 0; j < cols; ++j) {

                result.data[i][j] = data[i][j] + other.data[i][j];

            }

        }

        return result;

    }

};


void matrix_class_demo() {

    std::cout << "=== 完整Matrix类演示 ===" << std::endl;

    

    try {

        // 创建矩阵

        Matrix m1(2, 3);

        m1.at(0, 0) = 1.0; m1.at(0, 1) = 2.0; m1.at(0, 2) = 3.0;

        m1.at(1, 0) = 4.0; m1.at(1, 1) = 5.0; m1.at(1, 2) = 6.0;

        

        // 拷贝构造

        Matrix m2 = m1;

        

        // 移动构造

        Matrix m3 = std::move(Matrix(2, 3));

        

        // 拷贝赋值

        Matrix m4(1, 1);

        m4 = m1;

        

        // 移动赋值

        Matrix m5;

        m5 = Matrix(3, 2);

        

        // 矩阵运算

        Matrix sum = m1 + m2;

        

        // 容器中的Matrix

        std::vector<Matrix> matrices;

        matrices.reserve(3);

        matrices.push_back(std::move(m1));  // 移动构造

        matrices.emplace_back(2, 2);        // 直接构造

        

    } catch (const std::exception& e) {

        std::cerr << "错误: " << e.what() << std::endl;

    }

    

    std::cout << "演示完成" << std::endl;

}

```


## 最佳实践总结


### 默认成员函数的设计原则


1. **遵循三五/零法则**

   ```cpp

   // 三五法则:如果需要自定义析构函数、拷贝构造函数或拷贝赋值运算符中的任何一个,通常需要定义所有三个

   class RuleOfThree {

   public:

       ~RuleOfThree();                    // 自定义析构函数

       RuleOfThree(const RuleOfThree&);   // 自定义拷贝构造

       RuleOfThree& operator=(const RuleOfThree&);  // 自定义拷贝赋值

   };

   

   // 五法则:C++11增加移动操作

   class RuleOfFive {

   public:

       ~RuleOfFive();                     // 自定义析构函数

       RuleOfFive(const RuleOfFive&);     // 自定义拷贝构造

       RuleOfFive& operator=(const RuleOfFive&);  // 自定义拷贝赋值

       RuleOfFive(RuleOfFive&&);          // 自定义移动构造

       RuleOfFive& operator=(RuleOfFive&&);  // 自定义移动赋值

   };

   

   // 零法则:如果不需要自定义任何特殊成员函数,让编译器生成它们

   class RuleOfZero {

       // 编译器生成所有默认成员函数

   };

   ```


2. **移动操作的noexcept声明**

   ```cpp

   class ResourceHolder {

   public:

       // 移动操作应声明为noexcept,以便在标准库中优化

       ResourceHolder(ResourceHolder&& other) noexcept;

       ResourceHolder& operator=(ResourceHolder&& other) noexcept;

   };

   ```


3. **使用=default和=delete**

   ```cpp

   class SmartDefaults {

   public:

       SmartDefaults() = default;                    // 显式默认构造

       SmartDefaults(const SmartDefaults&) = delete; // 禁止拷贝

       SmartDefaults& operator=(const SmartDefaults&) = delete;

       SmartDefaults(SmartDefaults&&) = default;     // 默认移动

   };

   ```


### 资源管理建议


1. **使用RAII管理资源**

   ```cpp

   class FileHandle {

       FILE* handle;

   public:

       explicit FileHandle(const char* filename) 

           : handle(fopen(filename, "r")) {

           if (!handle) throw std::runtime_error("无法打开文件");

       }

       

       ~FileHandle() { if (handle) fclose(handle); }

       

       // 禁用拷贝,启用移动

       FileHandle(const FileHandle&) = delete;

       FileHandle(FileHandle&& other) noexcept 

           : handle(other.handle) {

           other.handle = nullptr;

       }

   };

   ```


2. **优先使用初始化列表**

   ```cpp

   class ProperInitialization {

       const int id;

       std::vector<int> data;

   public:

       ProperInitialization(int id_val, size_t size)

           : id(id_val), data(size) {  // 正确:初始化列表

           // 不要在这里初始化const成员或引用

       }

   };

   ```


通过深入理解C++的6个默认成员函数,开发者可以编写出更安全、更高效的类设计。这些函数共同定义了对象的创建、复制、移动和销毁行为,是C++面向对象编程的基石。掌握它们的实现原理和最佳实践,对于构建健壮的C++应用程序至关重要。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3106783/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933493