C++学习笔记——(三)深拷贝和浅拷贝

本文详细介绍了C++中的拷贝构造函数、赋值操作、函数返回对象以及对象作为函数参数传递时的内存管理。通过实例展示了浅拷贝可能导致的问题,并通过深拷贝解决了这一问题。文章还讨论了如何避免重复释放内存导致的错误,以及默认拷贝构造函数和自定义拷贝构造函数的区别。
摘要由CSDN通过智能技术生成

注:编码工具是CLion+Cygwin64

目录

拷贝构造函数

赋值

函数返回对象

对象作为函数参数传递

浅拷贝

深拷贝


拷贝构造函数

        C++在给一个对象引用赋值、函数返回对象或者对象作为函数参数传递的时候,会调用类的拷贝构造函数创建一个新的对象。

赋值

#include <iostream>

using namespace std;

class Student {
public:
    char *name;

    Student(char *name) : name(name) {
        cout << "构造函数" << endl;
    }

    Student(const Student &student) {
        cout << "自定义拷贝构造函数" << endl;
        this->name = student.name;
    }
};

int main() {
    Student stu("Yes");
    Student copy = stu;
    cout << "stu.name = " << stu.name << ", copy.name = "
         << copy.name << endl;
    printf("stu的地址是%p, copy的地址是%p\n", &stu, &copy);
    return 0;
}

输出:

构造函数
自定义拷贝构造函数
stu.name = Yes, copy.name = Yes
stu的地址是0xffffcc38, copy的地址是0xffffcc30

函数返回对象

        在CLion上演示不出来效果,不调用拷贝构造函数,在Visual Studio上可以。

对象作为函数参数传递

#include <iostream>
#include <cstring>

using namespace std;

class Student{
public:
    char * name;
    Student(char * name):name(name){
        printf("调用了构造函数\n\n");
    }
    Student(const Student & student){
        printf("调用了拷贝构造函数:\n");
        this->name = student.name;
        printf("this的地址是%p\nstudent的地址是%p\n", this, &student);
        printf("this->name的地址是%p\nstudent.name的地址是%p\n\n",
               this->name, student.name);
    }
};

void showStudent(Student student){
    cout << "showStudent函数中student.name = " << student.name << endl;
    printf("showStudent函数中student.name的地址是%p\n", student.name);
}

int main(){
    Student student("Catch");
    showStudent(student);
    
    cout << "main函数中student.name = " << student.name << endl;
    printf("main函数中student.name的地址是%p\n", student.name);
    return 0;
}

输出:

调用了构造函数

调用了拷贝构造函数:
this的地址是0xffffcc38
student的地址是0xffffcc30
this->name的地址是0x1004030f8
student.name的地址是0x1004030f8

showStudent函数中student.name = Catch
showStudent函数中student.name的地址是0x1004030f8
main函数中student.name = Catch
main函数中student.name的地址是0x1004030f8

        从输出内容中的地址可以看出来,main函数中和showStudent函数中的student不是一个对象,因为它们的内存地址不同。但注意看,两个student对象的成员属性name的内存地址是相同的。

浅拷贝

        类的默认拷贝构造函数和自定义的不使用动态开辟内存方式创建变量的拷贝构造函数的成员拷贝方式都是浅拷贝。上面说的几种情况都是浅拷贝。

        浅拷贝会引发一个问题:重复释放相同地址内存。当被拷贝的对象的成员属性是动态开辟内存方式创建的,在析构函数会进行释放,但是浅拷贝方式实现的拷贝构造函数会让对象成员属性直接保存被拷贝对象的成员属性所保存的内存地址,这样就造成了,当两个对象有一个被释放,该成员属性就会因调用析构函数而被释放,而另一个对象再被释放调用析构函数时,因为该属性已经被释放过了,就会重复释放,接着就报错了。

错误示例:

#include <iostream>
#include <cstring>

using namespace std;

class Student{
public:
    char * name;
    Student(char * name){
        cout << "构造函数被调用" << endl;
        this->name = (char*)malloc(sizeof(char) * 10);
        strcpy(this->name, name);
    }

    Student(const Student & stu){
        cout << "拷贝构造函数被调用" << endl;
        this->name = stu.name;
    }

    ~Student(){
        cout << "析构函数被调用" << endl;
        free(name);
        name = NULL;
    }
};

int main(){
    Student stu("Telepathy");
    Student copy = stu;
    return 0;
}

深拷贝

        为拷贝对象的成员属性用动态开辟内存方式创建内存,解决因浅拷贝造成的重复释放相同内存问题。

#include <iostream>
#include <cstring>

using namespace std;

class Student{
public:
    char * name;
    Student(char * name){
        cout << "构造函数被调用" << endl;
        this->name = (char*)malloc(sizeof(char) * 10);
        strcpy(this->name, name);
    }

    Student(const Student & stu){
        cout << "拷贝构造函数被调用" << endl;
//        this->name = stu.name;
        // 深拷贝
        this->name = (char*) malloc(sizeof(char) * 10);
        strcpy(this->name, stu.name);
    }

    ~Student(){
        cout << "析构函数被调用" << endl;
        free(name);
        name = NULL;
    }
};

int main(){
    Student stu("Telepathy");
    Student copy = stu;
    return 0;
}

        与浅拷贝的错误示例的不同之处在拷贝构造函数中,用动态开辟内存的方式替代了直接赋值的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值