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

281

被折叠的 条评论
为什么被折叠?



