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

C++拷贝构造

转换构造

一个类中构造函数可以重载,允许多个存在。如果构造函数只有一个参数且非当前类对时,可以将其他类型自动转换为当前类类型,这个过程为隐式类型转换。如下示例中的三个构造函数都可以发生隐式类型转换,在使用等号初始化或赋值时自动转换。
这样的可以发生隐式类型转换的构造函数可以称之为:转换构造函数。

class CTest {CTest(int a) {} // 转换构造函数/*CTest(int a, int b = 0) {} // 转换构造函数CTest(int a = 0, int b = 0) {} // 转换构造函数explicit CTest(int a = 0, int b = 0) {} // 禁止发生隐式类型转换CTest(int a, int b) {} // 不是转换构造函数*/
};
CTest tst(10); // 调用带参数的构造
CTest tst2 = 20; // 合法操作 发生隐式类型转换,将int类型转换为CTest类型
tst2 = 30; // 合法操作 发生隐式类型转换

 

  • 转换构造函数:只定义了一个接受一个 int 参数的构造函数,允许隐式类型转换。
  • 取消了其他构造函数的注释后,下面是解释:
    • CTest(int a, int b = 0)可以通过一个参数隐式转换,第二个参数使用默认值。
    • CTest(int a = 0, int b = 0)同样可以进行隐式转换,但两个参数都有默认值。
    • explicit CTest(int a = 0, int b = 0)禁止隐式类型转换。只有在显式调用构造函数时才能构造对象。
    • CTest(int a, int b)此构造函数需要两个参数,因此不能用来进行隐式转换。
    • CTest tst(10);这是合法的,调用了 CTest(int a) 构造函数,创建一个 CTest 对象 tst
    • CTest tst2 = 20;这里发生了隐式类型转换,20 被转换为 CTest 类型,调用了 CTest(int a) 构造函数来初始化 tst2。这是合法的,因为你定义了一个可以接受单个 int 参数的构造函数。
    • tst2 = 30;这是合法的,30 被隐式转换为 CTest 类型,赋值给 tst2。同样使用了 CTest(int a) 构造函数。

注意:如果是多个参数且无默认值时,则不能自动隐式类型转换。如果想要避免隐式类型转换,在构造函数前加上 关键字:explicit。

拷贝构造函数

拷贝构造函数:是众多构造函数中的一种,空类中编译期会默认提供的一个函数,它
与默认的无参构造并存,函数名为当前类名,参数为当前类对象的引用(const 类名&
类对象),函数体代码不为空,代码为形参对象中的成员属性依次给this对象成员属性
初始化,一旦我们手动重构了拷贝构造,编译期就不会提供默认的了。当然也不会存
在默认的无参构造了。当用一个类对象给类的另一个对象初始化时,会调用拷贝构造函数。

对上述话以此拆分讲述

参数为当前类对象的引用。与默认无参构造不同,其函数体代码一般不为空,操作为:参数中对象成员依次给this对象成员进行初始化。

class CTest {int m_a;
//默认的拷贝构造函数一般长这个样子,对于初始化代码下面两种写法都可以CTest(const CTest & tst):m_a(tst.m_a) {//初始化参数列表
//this->m_a = tst.m_a;//或者再构造体内}
};

 上述代码解析:拷贝构造函数的主要作用是通过已存在的对象来初始化一个新对象。在这个例子中,定义的拷贝构造函数接受一个 CTest 类型的常量引用参数 tst,并使用这个对象的 m_a 成员变量来初始化新对象的 m_a 成员变量。


当我们手动重构拷贝构造函数时,编译器就不会提供默认的拷贝构造函数了,当然也不会存在默认的无参构造了。
当用一个类对象给类的另一个对象初始化时,会调用拷贝构造函数。 

class CTest {CTest();CTest(const CTest &tst);
};
CTest tst1; //调用无参构造
CTest tst2(tst1); //调用拷贝构造

 默认的拷贝构造函数:是一个浅拷贝
浅拷贝需注意的问题:当类中存在指针成员且指向了一个具体的空间,拷贝构造函数
只是将两个指针里存储的地址进行一个值传递,并不会处理指针指向的空间。这样就
导致了多个对象里的指针指向了同一个空间,那么会导致以下两个问题:

  1. 数据一致性问题

    • 当一个对象通过指针修改了其指向的内存空间的内容时,其他对象访问同一内存空间时会得到已修改的值。这种情况下,修改的意图和结果可能不符合预期。如果多个对象共享同一数据,可能会导致意外的副作用。
  2. 内存管理问题

    • 如果对象的析构函数试图释放new出来的空间,释放指针所指向的内存空间,而多个对象指向同一地址,将导致后续的对象尝试释放已经被释放的内存,从而引发未定义行为(比如崩溃或内存泄漏)。

 

 解决浅拷贝的问题

解决办法: 深拷贝,它并不是一个固定的写法,而是一个解决的办法:即在拷贝构造时,如果参数对象中的指针成员指向了一个内存空间,那么在重构拷贝构造时,需要为当前this对象中指针成员额外开辟新的内存空间,并初始化对应的值。

即:深拷贝会为每个对象的指针成员分配新的内存空间,并复制指针指向的内容。

class CTest {int *m_p;CTest(const CTest & tst) {//深拷贝if (tst.m_p)m_p = new int(*tst.m_p);elsem_p = nullptr;}
};

 在某些情况下,可以使用指针或引用可以避免对象的值传递,也避免了浅拷贝问题。

void fun(CTest tst); //避免值传递
void fun(CTest & tst);// or void fun(CTest *ptst);

 例子:

class MyClass {
private:int* data;
public:// 构造函数MyClass(int value) {data = new int(value);}// 拷贝构造函数MyClass(const MyClass& other) {data = new int(*other.data); // 深拷贝}// 析构函数~MyClass() {delete data; // 释放内存}// 赋值操作符重载(如果需要)MyClass& operator=(const MyClass& other) {if (this != &other) {delete data; // 先释放原有内存data = new int(*other.data); // 深拷贝}return *this;}
};

赋值函数operator=

operator=与拷贝构造的区别:

拷贝构造发生在构造对象的时候,operator=是赋值。


空类中会默认提供一个 operator=函数,函数和返回值 为当前类对象的引用,函数体代码不为空,形参对象中的成员属性依次给this对象成员属性赋值,用当前类对象给另一个类对象进行赋值操作。如果手动重构了operator=,编译器就不会提供默认的了。

class CTest {public:int m_a;CTest& operator=(const CTest& tst) {this->m_a = tst.m_a;}
};
CTest tst1;
CTest tst2;
tst2 = tst1; //匹配operator =

operator=函数 默认也是一个浅拷贝,也会出现浅拷贝问题。解决办法:深拷贝 。

int * m_p;
CTest& operator=(const CTest& tst) {if(this != &tst) { //判断是否自己给自己赋值this->m_a = tst.m_a;if (tst.m_p) {if (this->m_p) {*this->m_p = *tst.m_p;} else {this->m_p = new int(*tst.m_p);}} else {if (this->m_p) {delete this->m_p;}this->m_p = nullptr;}}
}

空类中存在的默认的函数4个:

  1. 默认无参数构造
  2. 默认的拷贝构造
  3. 默认的operator=
  4. 默认析构函数
http://www.lryc.cn/news/591697.html

相关文章:

  • 嵌入式学习-PyTorch(7)-day23
  • 突破限制:使用 Claude Code Proxy 让 Claude Code 自由连接任意模型
  • 【分治思想解题框架】【分解、求解、合并】
  • 如何在KL散度的意义下解释极大似然估计(二)
  • Kiro vs Cursor: AI IDE 终极对比指南
  • Redis7 底层数据结构解析
  • 数据呈现高阶技巧:散点图与桑基图的独特价值
  • 在CentOS7.9服务器上安装.NET 8.0 SDK
  • .Net将控制台的输出信息存入到日志文件按分钟生成日志文件
  • 编译原理第六到七章(知识点学习/期末复习/笔试/面试)
  • Docker-Beta?ollama的完美替代品
  • 如何将 ONLYOFFICE 文档集成到使用 Laravel 框架编写的 PHP 网络应用程序中
  • NO.7数据结构树|线索二叉树|树森林二叉树转化|树森林遍历|并查集|二叉排序树|平衡二叉树|哈夫曼树|哈夫曼编码
  • vue3 自定义vant-calendar header/footer/maincontent
  • Java数组中相关查找排序算法(1)
  • 力扣 hot100 Day47
  • 网络安全威胁下的企业困境与破局技术实践
  • Linux内核内存管理相关的配置参数
  • 电商行业如何做好网络安全工作?
  • 【web安全】DVWA反射型XSS漏洞分析与利用
  • RGBA图片格式转换为RGB格式(解决convert转换的失真问题)
  • 利用node.js在本地搭建简易http服务器
  • 快慢指针的应用
  • RCU机制及常见锁的理解
  • web安全入门 | 记新手小白初次尝试挖越权漏洞
  • Ansible AWX 自动化运维
  • 3t车用手动卧式千斤顶设计含8张CAD图纸PDF图
  • parallels desktop windows win10无法复制文件无法共享剪切板
  • [NIPST AI]对抗性机器学习攻击和缓解的分类和术语
  • RocketMq集群高可用