复制与拷贝构造函数 in C++【C++学习笔记】

本文详细阐述了C++中浅拷贝(浅度复制)与深拷贝(深度复制)的概念,通过实例讲解了为何浅拷贝会导致内存泄漏,并介绍了如何通过自定义拷贝构造函数实现深拷贝,以确保对象间数据独立。还讨论了浅拷贝在值传递和引用传递中的行为差异,以及何时选择使用const引用来避免不必要的复制。

45复制与拷贝构造函数 in C++

除了引用外,当编写一个变量被赋值给另一个变量的代码时,你总是在复制

🍅浅度复制(浅拷贝)

class Entity
{
    int *ptr;
    Entity(const int &temp) {
        ptr = new int(temp);
    }
    ~Entity() {
        delete ptr;
    }
};
int main()
{
    int temp = 1;
    Entity a(temp);
    Entity b = a;//浅拷贝
    //将a对象里的成员都复制到了b,注意这个做法是很危险的!
    return 0;
}

像上面的把a赋值给了b,编译器是不会报错的,但是会运行出错!

因为b的成员ptr和a的成员ptr都指向了同一块内存地址,当作用域结束时,a的析构函数就会释放a的成员ptr所指向的内存地址的空间,但是b也指向这一个内存地址。所以当a的操作结束轮到b的时候,b的析构函数也会执行一次释放内存地址的操作,但这个时候的内存地址已经空了,相当于delete ptr会被执行两次!所以程序就会崩溃!

我们真正想做的,是创建一个新的对象储存新的东西,但是现在两个对象指向完全相同的内存缓冲区,想改变其中一个,就会牵连另外一个。

我们想要复制内存块,我们希望第二个对象拥有自己的指针,以拥有自己唯一的内存块,当我们修改或删除第二个对象的数据时,它不会触及第一个对象,反之亦然

能做到这一点的方法,是执行一种叫做**深度复制(深拷贝)**的东西,也就是说我们实际上复制了整个对象,而不是上面的浅拷贝

浅拷贝不会去到指针的内容或者指针所指向的方向,也不会去复制它,而深拷贝是复制整个对象,包括内存块

🍅深拷贝

深拷贝的方法是:拷贝构造函数

  • 拷贝构造函数是一个构造函数。当我复制第二个对象的时候,它就会被调用。当你试图创建一个新的变量并给他分配另一个变量时,这两个变量有相同的类型,则会复制出这个变量,这就是所谓的拷贝构造函数

C++在默认情况下会提供一个拷贝构造函数

class Entity
{
public:
    int *ptr;
    int num;
    Entity(const Entity &other) 	//other只是一个名字,可以随便换
        : ptr(other.ptr), num(other.num) {}
    //以上是 理 论 上 C++默认的拷贝构造函数的样子了   
    //这个默认函数做的就是内存复制,将other对象的内存浅层拷贝进这些成员变量	
   	
};

但上面那种肯定是不行的,因为我们不仅仅想复制指针,我们还想复制指针所指向的内存!

如果我们决定不需要拷贝构造函数,不允许复制,那可以把这个拷贝构造函数声明为delete,如下

Entity(const Entity &other) = delete;

(题外话,unique_ptr所做的就是禁用了拷贝构造函数,使得它无法被复制,算是一种保护措施)

所以这里要做的是,找到我们自己的拷贝构造函数,进行深拷贝

class Entity
{
public:
    int *ptr;
    int num;
    Entity(const Entity &other) 
        : num(other.num)
    {
        ptr = new int(num);
    }
    //我们知道我们是要从other中复制,所以我们真正要做的就是复制other的缓冲区!
};

That is it,这就是执行深拷贝所必须使用的代码

  • 浅拷贝是接受一个值的时候(由构造函数得)创建一个内存块,而接受一个类对象的时候只是进行单纯的复制
  • 深拷贝是接受一个值的时候创建一个内存块,接受一个类对象的时候也创建一个内存块进行储存

深拷贝这样做就使得两个类直接互不干扰了,各用各的内存块

那么拷贝只会在a = b这种情况下发生吗

显然不会

class Entity
{
public:
    int *ptr;
    int num;
    Entity(const Entity &other) 
        : num(other.num)
    {
        ptr = new int(num);
    }
};
void Print(Entity) {	//注意这里形参不是引用形式,所以就会发生值传递,即有复制的发生!
    std::cout << Entity.(*ptr) << std::endl;
}
int main()
{
    Entity a;
    auto b = a;	//复制一次
    Print(a);	//复制两次
    Print(b);	//复制三次
    return 0;
}

我想做的仅仅只是单纯打印一点东西而已,但是却莫名其妙的多进行了几次复制的操作从而降低了运行速度(多了副本)。我想做的是将现有的字符串直接放进这个Print()函数中

而解决方法是:直接传引用(开头说过,一个变量赋值给另一个变量,除了引用外,都会进行复制操作,所以这里只有引用)

💡具体来说是标记为const引用

void Print(const Entity &temp) {	//引用可将对象直接放进函数中,而const则是承诺只能对对象进行只读操作,不能进行修改操作,让对象变得安全
   	std::cout << temp.(*ptr) << std::endl;
}

💡所以,如果形参的作用只是只读,则应保持通过 const引用 去传递形参or对象(当然这个操作其实是可以优化的,并不是说每时每刻const引用就更快,某些情况下复制可能更快。但无论如何,在基础使用中,用const引用更好)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值