当前位置: 首页 > news >正文

c++的拷贝构造函数和赋值函数

拷贝构造函数和赋值函数

什么是拷贝构造

是一种特殊构造函数,如果没有显式的实现,编译器就会自动生成。

class 类名
{
public:// 拷贝构造类名(const 类名& that){}
};
什么时候会调用拷贝构造

当使用一个类对象给另一个新的类对象初始化时,就会自动调用拷贝构造。

#include <iostream>
using namespace std;
​
class Test
{
public:Test(void){   cout << "调用了普通的构造函数" << endl;}   Test(const Test& that){   cout << "调用了拷贝构造" << endl;}   
};
​
void func(Test t)
{
​
}
​
int main(int argc,const char* argv[])
{Test t1;        // 调用的是普通构造Test t2 = t1;   // 调用的是拷贝构造func(t1);       // 调用的是拷贝构造return 0;
}
拷贝构造的任务是什么

拷贝构造参数对象的所有成员变量挨个赋值给新对象的成员变量,一般情况下编译器自动生成的拷贝构造就能完全满足我们使用需求。

什么时候需要显式实现拷贝构造

当成员变量中有指针成员且指向了堆内存,就需要显式实现拷贝构造。

编译器自动生成的拷贝构造,只会对成员变量挨个赋值,如果成员变量中有指针变量且指向堆内存,结果就两个对象的指针变量同时指向一份堆内存,当它们执行析构函数时,会把这块堆内存释放两次,产生 double free or corruption 的错误。

正确的做法应该是先给新对象的指针变量重新申请一份堆内存,然后把旧对象的指针变量所指向的内存拷贝到新对象的指针变量所指向的内存。

#include <iostream>
using namespace std;
​
class Test
{int* ptr;
public:Test(int num){ptr = new int;cout << "new:" << ptr << endl;*ptr = num;}
​~Test(void){cout << "delete:" << ptr << endl;delete ptr;}
​/* 编译器生成的拷贝构造,会造成 double freeTest(const Test& that){ptr = that.ptr; }*/Test(const Test& that){// 给新对象的指针变量重新申请堆内存ptr = new int(*that.ptr);// 把旧对象的指针变量所指向的内存拷贝给新对象的指针变量所指向的内存,如果不方便解引用时可以使用memcpy函数}
​void show(void){cout << "val:" << *ptr << " addr:" << ptr << endl;}
};
​
int main(int argc,const char* argv[])
{Test t1(12345);Test t2 = t1;t1.show();t2.show();
​return 0;
}
什么是赋值函数

是一种特殊的成员函数,如果没有显式实现,编译器会自动生成。

class 类名
{
public:// 赋值函数const 类名& operator=(const 类名& that){}
};
什么时候会调用赋值函数

当一个旧对象给另一个旧对象赋值时会自动调用赋值函数。

当一个旧对象给另一个新对象初始化时会自动调用拷贝构造函数。

#include <iostream>
using namespace std;
​
class Test
{
public:Test(const Test& that){   cout << "调用了拷贝构造" << endl;}   
​void operator=(const Test& that){   cout << "调用了赋值函数" << endl;}   
};
​
int main(int argc,const char* argv[])
{Test t1;        // 调用了普通的构造函数Test t2 = t1;   // 调用了拷贝构造t1 = t2;        // 调用的是赋值函数return 0;
}
赋值函数的任务是什么

赋值函数与拷贝构造的任务几乎相同,都是挨个给成员变量赋值,但如果需要显式实现时,它的业务逻辑不同。

什么时候需要显式实现赋值函数

当需要显式实现拷贝构造时,就需要显式实现赋值函数,它们两个面临问题是一样的。

赋值函数不应该对成员指针变量赋值,而应该对象成员指针变量所指向的内存进行拷贝。

#include <iostream>
using namespace std;
​
class Test
{int* ptr;
public:Test(int num){ptr = new int;cout << "new " << ptr << endl;*ptr = num;}~Test(void){cout << "delete " << ptr << endl;// delete ptr;}
​Test(const Test& that){ptr = new int;// 如果不方便解引用,可以调用memcpy函数进行拷贝*ptr = *that.ptr;cout << "new " << ptr << "调用了拷贝构造" << endl;}
​const Test& operator=(const Test& that){// 当ptr和that.ptr指向的内存块大小一样,可以直接进行内存拷贝*ptr = *that.ptr;cout << "调用了赋值函数" << endl;return *this;/*当对象的ptr指向的内存与与that.ptr指向的内存块不一样大先释放旧的ptr再分配新的,要与that.ptr的内存块一样大然后再拷贝*/}
};
​
int main(int argc,const char* argv[])
{Test t1(1234);      // 调用了普通的构造函数Test t2 = t1;   // 调用了拷贝构造t1 = t2;        // 调用的是赋值函数return 0;
}
浅拷贝与深拷贝

拷贝就是一个对象给另一个对象赋值,编译器自动生成的拷贝构造和赋值函数执行的业务逻辑就是浅拷贝(成员指针给成员指针赋值),深拷贝就是把成员指针所指向的内存拷贝给另一个成员指针所指向的内存。

浅拷贝就是指针给指针赋值,深拷贝就内存给内存赋值。

注意:如果成员变量中没有成员指针,则浅拷贝就可以满足需求,如果如果成员变量中有成员指针且指向堆内存,则必须手动实现深拷贝,否则就会出现 double free or corruption 的错误。

http://www.lryc.cn/news/434618.html

相关文章:

  • 什么自动猫砂盆才适合旅游党?4个选购技巧统统告诉你!
  • 算法知识点————双指针【删除重复元素】【反转链表】
  • 建造者模式builder
  • IEC103设备数据 转 IEC61850项目案例
  • 438.找到字符串中所有字母异位词
  • Microsoft SC-100: Microsoft 网络安全架构师
  • 代码随想录训练营day42|188.买卖股票的最佳时机IV,309.最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费
  • 解决Pynput不能在Ubuntu22.04上正常使用问题
  • IPV4端口数据有哪些?
  • 【爱加密_云平台-注册/登录安全分析报告】
  • Open CASCADE学习|按圆离散旋转体
  • 无人矿车使用ZMQ消息代理进行跨机互联进行消息收发
  • 医疗机构关于DIP/DRG信息化建设
  • 100个候选人,没一个能讲明白什么是自动化框架?
  • 数据结构与算法1: 链表
  • 【专题】2024年8月医药行业报告合集汇总PDF分享(附原数据表)
  • 这10种人不适合干项目经理,你在其中吗?
  • IT每日英语(三)
  • 【保姆级教程】如何创建一个vitepress项目?
  • 智能头盔语音识别声控芯片,AI离线语音识别ic方案,NRK3301
  • 【STM32】CAN总线基础入门
  • STM32F1+HAL库+FreeTOTS学习10——任务相关API函数使用
  • 华为 HCIP-Datacom H12-821 题库 (14)
  • java八股!2
  • 一分钟了解统一软件开发过程RUP的那点事
  • Goby 漏洞发布|(CVE-2024-45195)Apache OFBiz /viewdatafile 代码执行漏洞【已复现】
  • js的书写位置和css的书写位置的区别?为什么要这样写?
  • Python一些可能用的到的函数系列132 ORM-sqlalchemy连clickhouse
  • 华为 HCIP-Datacom H12-821 题库 (12)
  • pointpillar部署-TensorRT实现(三)