Skip to content

深拷贝和浅拷贝

此概念针对的是对象之间的拷贝(拷贝构造,拷贝赋值)及对象内部的指针告别问题。

浅拷贝

  • 行为: 当复制一个对象时,只复制对象本身的成员变量的值。如果成员变量是一个指针,那么只复制这个指针的地址值,而不是指针所指向的内存中的内容。

  • 结果: 复制后,原对象和新对象的指针成员将指向同一块内存区域。

  • 默认行为: C++ 编译器默认生成的拷贝构造函数和拷贝赋值运算符执行的是浅拷贝。

深拷贝

  • 行为: 当复制一个对象时,不仅复制对象本身的成员变量的值,如果成员变量是一个指针,深拷贝会为新对象分配一块新的内存,并将原对象指针所指向的内存中的内容复制到这块新内存中。
  • 结果: 复制后,原对象和新对象的指针成员将指向各自独立的内存区域,这两块内存区域的内容相同(在拷贝的时刻)。
  • 实现: 需要程序员显式地定义拷贝构造函数和拷贝赋值运算符来实现。

浅拷贝问题

指针重复释放

描述

  • print(const Student stu)发生了浅拷贝【使用编译器默认生成的拷贝构造】,导致stu1->datastu->data指针指向相同,随着print函数结束,执行delete销毁
  • 随着main结束,stu1销毁,发现data的指向已经销毁,出现double free
cpp
#include <iostream>
#include <cstring>

class Student
{
public:
    // 默认构造
    explicit Student(char *data, int length)
    {
        this->data = new char[length + 1];
        this->length = length;
        strcpy(this->data, data);
    }

    ~Student()
    {
        delete[] data;
        data = nullptr;
    }

    // 打印
    void print(const Student stu)
    {
        std::cout << stu.data << std::endl;
    }

public:
    char *data;
    int length;
};

int main()
{

    char *data = "hello world";
    Student stu1(data, 10);
    stu1.print(stu1);
    return 0;
}

解决

  • 根据Rule of Three/Rule of Five,我们自定义了任何一个(析构,拷贝构造,拷贝赋值,移动构造,移动赋值),都应该自定义其他几个
  • 根据实际情况,这里我们也应该编写拷贝构造函数,在拷贝构造内部使用new分配空间
cpp
#include <iostream>
#include <cstring>

class Student
{
public:
    // 默认构造
    explicit Student(char *data, int length)
    {
        this->data = new char[length + 1];
        this->length = length;
        strcpy(this->data, data);
    }

    // 拷贝构造
    Student(const Student &stu)
    {
        this->data = new char[stu.length + 1];
        this->length = stu.length;
        strcpy(this->data, stu.data);
    }

    ~Student()
    {
        delete[] data;
        data = nullptr;
    }

    // 打印
    void print(const Student stu)
    {
        std::cout << stu.data << std::endl;
    }

public:
    char *data;
    int length;
};

int main()
{

    char *data = "hello world";
    Student stu1(data, 10);
    stu1.print(stu1);
    return 0;
}