C++中的拷贝构造是怎样的

发布时间:2022-01-17 13:35:43 作者:柒染
来源:亿速云 阅读:162
# C++中的拷贝构造是怎样的

## 1. 拷贝构造函数的基本概念

### 1.1 什么是拷贝构造函数

拷贝构造函数是C++中一种特殊的构造函数,它用于**创建一个新对象作为现有对象的副本**。当我们需要用一个已存在的对象初始化同类型的新对象时,拷贝构造函数就会被调用。

```cpp
class MyClass {
public:
    // 拷贝构造函数声明
    MyClass(const MyClass& other);
};

1.2 拷贝构造函数的特征

1.3 何时会调用拷贝构造函数

拷贝构造函数在以下场景会被自动调用:

  1. 显式初始化:用已有对象初始化新对象

    MyClass obj1;
    MyClass obj2 = obj1;  // 调用拷贝构造函数
    
  2. 函数参数传递:对象作为函数参数按值传递时 “`cpp void func(MyClass obj);

MyClass original; func(original); // 调用拷贝构造函数


3. **函数返回对象**:函数返回对象时(可能被编译器优化)
   ```cpp
   MyClass createObject() {
       MyClass obj;
       return obj;  // 可能调用拷贝构造函数
   }

2. 默认拷贝构造函数

2.1 编译器生成的默认拷贝构造函数

如果类没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。这个默认实现会执行浅拷贝(shallow copy),即:

2.2 默认拷贝构造函数的局限性

当类包含动态分配的资源(如堆内存、文件句柄等)时,默认拷贝构造函数会导致问题:

class ProblemClass {
public:
    int* data;
    ProblemClass(int size) { data = new int[size]; }
    ~ProblemClass() { delete[] data; }
    // 没有自定义拷贝构造函数
};

void demo() {
    ProblemClass obj1(10);
    ProblemClass obj2 = obj1;  // 浅拷贝,两个对象指向同一内存
    // 析构时会导致双重释放错误!
}

3. 自定义拷贝构造函数

3.1 实现深拷贝

对于需要管理资源的类,必须自定义拷贝构造函数实现深拷贝(deep copy):

class SafeArray {
public:
    int* ptr;
    int size;
    
    // 自定义拷贝构造函数
    SafeArray(const SafeArray& other) : size(other.size) {
        ptr = new int[size];
        for(int i = 0; i < size; ++i) {
            ptr[i] = other.ptr[i];
        }
    }
    
    // 构造函数
    SafeArray(int s) : size(s) {
        ptr = new int[size];
    }
    
    // 析构函数
    ~SafeArray() {
        delete[] ptr;
    }
};

3.2 拷贝构造函数的注意事项

  1. 参数必须是引用:否则会导致无限递归调用

    // 错误!会导致无限递归
    MyClass(MyClass other);
    
  2. 处理自赋值情况:虽然拷贝构造中不常见,但应考虑

  3. 保持const正确性:源对象通常应为const引用

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

4.1 区别与联系

特性 拷贝构造函数 赋值运算符
调用时机 创建新对象时 已存在对象赋值时
语法 ClassName(const ClassName&) ClassName& operator=(const ClassName&)
返回值 通常返回*this
默认行为 成员逐个拷贝 成员逐个赋值

4.2 三法则(Rule of Three)

如果一个类需要定义以下任何一个特殊成员函数,那么通常需要同时定义这三个:

  1. 析构函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
class RuleOfThree {
public:
    // 1. 析构函数
    ~RuleOfThree() { /* 释放资源 */ }
    
    // 2. 拷贝构造函数
    RuleOfThree(const RuleOfThree& other) { /* 深拷贝实现 */ }
    
    // 3. 拷贝赋值运算符
    RuleOfThree& operator=(const RuleOfThree& other) {
        if(this != &other) {
            /* 释放现有资源并深拷贝 */
        }
        return *this;
    }
};

5. 现代C++中的拷贝控制

5.1 五法则(Rule of Five)

C++11引入了移动语义,扩展为五法则:

  1. 析构函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
  4. 移动构造函数
  5. 移动赋值运算符
class RuleOfFive {
public:
    // 1. 析构函数
    ~RuleOfFive();
    
    // 2. 拷贝构造函数
    RuleOfFive(const RuleOfFive&);
    
    // 3. 拷贝赋值运算符
    RuleOfFive& operator=(const RuleOfFive&);
    
    // 4. 移动构造函数
    RuleOfFive(RuleOfFive&&) noexcept;
    
    // 5. 移动赋值运算符
    RuleOfFive& operator=(RuleOfFive&&) noexcept;
};

5.2 零法则(Rule of Zero)

理想情况下,类的资源管理应该委托给智能指针等RI对象,这样编译器生成的默认特殊成员函数就能正确工作:

class RuleOfZero {
    std::unique_ptr<Resource> resource;  // 自动管理资源
    std::vector<int> items;              // 自动管理内存
    // 不需要定义任何特殊成员函数
};

6. 拷贝构造函数的性能优化

6.1 避免不必要的拷贝

  1. 使用const引用传递参数

    void processObject(const MyClass& obj);  // 避免拷贝
    
  2. 返回值优化(RVO/NRVO):现代编译器会自动优化返回临时对象的场景

6.2 拷贝省略(Copy Elision)

编译器在某些情况下可以省略拷贝构造函数的调用:

MyClass create() {
    return MyClass();  // 可能直接构造在调用处,不调用拷贝构造函数
}

7. 实际应用案例

7.1 字符串类的拷贝构造

class MyString {
public:
    char* data;
    size_t length;
    
    // 拷贝构造函数
    MyString(const MyString& other) : length(other.length) {
        data = new char[length + 1];
        strcpy(data, other.data);
    }
    
    // 构造函数
    MyString(const char* str = "") {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
    }
    
    // 析构函数
    ~MyString() {
        delete[] data;
    }
};

7.2 禁止拷贝的类

某些类不应该允许拷贝操作(如线程类):

class NonCopyable {
public:
    NonCopyable() = default;
    
    // 删除拷贝操作
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

8. 总结

掌握拷贝构造函数是成为C++高级开发者的重要一步,它直接关系到程序的正确性和性能。在实际开发中,应根据类的具体需求合理实现或禁用拷贝操作。 “`

这篇文章约2400字,涵盖了拷贝构造函数的核心概念、实现方法、相关规则和最佳实践,采用Markdown格式编写,包含代码示例和对比表格,便于理解和参考。

推荐阅读:
  1. C++ 拷贝构造
  2. C++拷贝构造函数是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c++

上一篇:如何解决leetcode中最后一个单词的长度问题

下一篇:原生js怎么实现下拉刷新和上拉加载更多

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》