授课语音

C++中的拷贝构造函数与赋值运算符重载

在C++中,拷贝构造函数和赋值运算符重载是两个重要的特性,它们主要用于控制对象如何复制,以避免默认行为导致的问题。正确理解和实现它们对管理资源和编写健壮的程序至关重要。


1. 拷贝构造函数

1.1 拷贝构造函数的定义

拷贝构造函数是一种特殊的构造函数,用于通过同类型的对象来初始化新对象。其语法如下:

class ClassName {
public:
    ClassName(const ClassName& obj);
};

1.2 作用与特点

  • 默认拷贝构造函数会逐成员复制对象的非静态成员(浅拷贝)。
  • 如果类中包含指针或需要深拷贝的资源,必须自定义拷贝构造函数。

1.3 拷贝构造函数的代码案例

#include <iostream>
#include <cstring>

class MyString {
private:
    char* data; // 动态分配的字符数组
    size_t length; // 字符串长度
public:
    // 构造函数
    MyString(const char* str) {
        length = strlen(str);
        data = new char[length + 1]; // 分配内存
        strcpy(data, str); // 复制字符串内容
        std::cout << "Constructor called for: " << data << std::endl;
    }

    // 拷贝构造函数
    MyString(const MyString& other) {
        length = other.length;
        data = new char[length + 1]; // 分配新内存
        strcpy(data, other.data); // 深拷贝数据
        std::cout << "Copy Constructor called for: " << data << std::endl;
    }

    // 打印字符串
    void print() const {
        std::cout << data << std::endl;
    }

    // 析构函数
    ~MyString() {
        std::cout << "Destructor called for: " << data << std::endl;
        delete[] data; // 释放内存
    }
};

int main() {
    MyString str1("Hello"); // 调用普通构造函数
    MyString str2 = str1;   // 调用拷贝构造函数
    str1.print();
    str2.print();
    return 0;
}

代码解析

  1. MyString(const MyString& other):通过 other 对象初始化当前对象。
  2. new char[length + 1]strcpy 实现深拷贝,避免多个对象共享同一块内存。
  3. 调用顺序展示了构造、拷贝构造和析构函数的执行情况。

2. 赋值运算符重载

2.1 赋值运算符的定义

赋值运算符重载用于定义两个已存在对象之间赋值操作的行为,其语法如下:

class ClassName {
public:
    ClassName& operator=(const ClassName& obj);
};

2.2 作用与特点

  • 默认赋值运算符也是逐成员复制(浅拷贝)。
  • 自定义赋值运算符需要考虑深拷贝、资源管理以及自赋值问题。

2.3 赋值运算符的代码案例

#include <iostream>
#include <cstring>

class MyString {
private:
    char* data;
    size_t length;
public:
    // 构造函数
    MyString(const char* str) {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
        std::cout << "Constructor called for: " << data << std::endl;
    }

    // 赋值运算符重载
    MyString& operator=(const MyString& other) {
        std::cout << "Assignment operator called for: " << other.data << std::endl;

        if (this == &other) {
            // 检查自赋值
            return *this;
        }

        // 释放原有资源
        delete[] data;

        // 深拷贝新资源
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);

        return *this;
    }

    // 打印字符串
    void print() const {
        std::cout << data << std::endl;
    }

    // 析构函数
    ~MyString() {
        std::cout << "Destructor called for: " << data << std::endl;
        delete[] data;
    }
};

int main() {
    MyString str1("Hello");
    MyString str2("World");

    str2 = str1; // 调用赋值运算符重载
    str1.print();
    str2.print();
    return 0;
}

代码解析

  1. 检查 if (this == &other) 防止自赋值。
  2. delete[] data 释放原资源,避免内存泄漏。
  3. 深拷贝 other.data,确保两个对象互不干扰。

3. 拷贝构造与赋值运算符的区别

特性 拷贝构造函数 赋值运算符重载
适用场景 初始化新对象 对现有对象赋值
是否需检查自赋值 不需要 需要
默认行为 浅拷贝 浅拷贝

3.1 实践建议

  1. 如果类中涉及动态内存分配或资源管理,必须实现深拷贝逻辑。
  2. 推荐遵循“拷贝构造函数、赋值运算符和析构函数同时实现或同时不实现”的原则(即“三法则”)。

通过掌握拷贝构造函数和赋值运算符重载,能够有效避免浅拷贝导致的内存管理问题,提高代码的健壮性和可维护性。

去1:1私密咨询

系列课程: