专业编程基础技术教程

网站首页 > 基础教程 正文

关于“拷贝构造函数”以及“浅拷贝”和“深拷贝”的问题

ccvgpt 2024-10-12 13:50:30 基础教程 8 ℃

默认情况下,c++编译器至少为我们的提供3个函数

1.默认构造函数(无参,函数体为空)

关于“拷贝构造函数”以及“浅拷贝”和“深拷贝”的问题

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对类中非静态成员属性简单拷贝

如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数

如果用户定义了普通构造(非拷贝),c++不再提供默认无参构造,但是会提供默认拷贝构造

其中“默认拷贝构造函数,对类中非静态成员属性简单值拷贝” 这一点指的是,当我们没有为类显式定义拷贝构造函数时,C++ 编译器会自动生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会逐个拷贝类中的非静态成员变量,但它仅仅做的是浅拷贝,即简单地将一个对象的成员变量的值赋给另一个对象。

1.拷贝构造函数的作用

拷贝构造函数是用来创建一个新对象,并使用已有的对象来初始化它。也就是说,拷贝构造函数定义了如何根据一个已有的对象来生成该对象的副本。

2.默认拷贝构造函数的行为

默认拷贝构造函数执行的是成员逐个拷贝(浅拷贝),即对于类中的每个非静态成员,直接调用它们各自的拷贝构造函数。编译器只会简单地将源对象的每个非静态成员的值赋给目标对象。

例如,假设我们有一个类如下:

class MyClass {
public:
    int a;
    float b;
};

如果你写代码如下:

MyClass obj1;
obj1.a = 10;
obj1.b = 5.5f;

MyClass obj2 = obj1;  // 调用默认拷贝构造函数

在这个过程中,编译器为 MyClass 自动生成的默认拷贝构造函数可能看起来像这样:

MyClass::MyClass(const MyClass& other) {
    this->a = other.a;  // 简单地将 obj1 的 a 赋值给 obj2 的 a
    this->b = other.b;  // 简单地将 obj1 的 b 赋值给 obj2 的 b
}

这里的 obj2 是通过拷贝 obj1 初始化的,obj2 的 a 和 b 成员的值分别是 obj1 中相应成员的值。

3.浅拷贝与深拷贝的区别

默认的拷贝构造函数进行的仅是浅拷贝。这在类的成员是基础数据类型(如 int、float)时没有问题,但如果类中有指针或动态分配的资源时,浅拷贝可能会引发问题。

class MyClass {
public:
    int* ptr;
    
    MyClass(int val) {
        ptr = new int(val);
    }

    ~MyClass() {
        delete ptr;
    }
};

假设我们有这样一个类,其中 ptr 是指向动态分配的内存。当我们执行拷贝时,默认的拷贝构造函数只会简单地复制指针的值,两个对象的 ptr 都指向同一块内存。这样会导致问题:

MyClass obj1(5);
MyClass obj2 = obj1;  // 调用默认拷贝构造函数,执行浅拷贝

其中obj1 和 obj2 的 ptr 指针指向同一块内存。如果 obj1 被析构,它会释放那块内存,而 obj2 仍然持有一个无效的指针。这就会导致双重释放(double free)或悬空指针问题。

4.解决方案:深拷贝

为了避免指针或动态资源的共享问题,当类涉及到动态资源时,应该自己定义拷贝构造函数,执行深拷贝,即为每个对象单独分配新的内存,并拷贝数据。

MyClass::MyClass(const MyClass& other) {
    ptr = new int(*other.ptr);  // 分配新的内存,并拷贝内容
}

这样,每个对象都有自己独立的资源,互不干扰。

so

默认拷贝构造函数进行的是浅拷贝,即对类中的非静态成员做简单的逐成员赋值。

  • 浅拷贝对基础类型(如 int、float)没有问题,但对于指针或动态分配的内存,可能会导致资源共享问题,进而引发错误。
  • 如果类中包含指针或动态分配的资源,通常需要自己定义拷贝构造函数,执行深拷贝,确保每个对象有独立的资源。
  • 最近发表
    标签列表