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

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.xdnf.cn/news/15647.html

相关文章:

  • 【数据结构】栈和队列
  • 李宏毅《生成式人工智能导论》 | 第15讲-第18讲:生成的策略-影像有关的生成式AI
  • 【读论文】AgentOrchestra 解读:LLM 智能体学会「团队协作」去解决复杂任务
  • 河南萌新联赛2025第一场-河南工业大学
  • Python--plist文件的读取
  • 【Linux】LVS(Linux virual server)
  • python-字典、集合、序列切片、字符串操作(笔记)
  • 大型语言模型的白日梦循环
  • Git简介与特点:从Linux到分布式版本控制的革命
  • Python 网络爬虫 —— 代理服务器
  • github不能访问怎么办
  • echart设置trigger: ‘axis‘不显示hover效果
  • C 语言基础第 08 天:数组与冒泡排序
  • HTTPS的工作原理及DNS的工作过程
  • 相位中心偏置天线的SAR动目标检测
  • 基于Echarts的气象数据可视化网站系统的设计与实现(Python版)
  • 【LeetCode 热题 100】108. 将有序数组转换为二叉搜索树
  • Git 多人协作实战:从基础操作到分支管理全流程记录
  • 深入了解linux系统—— 信号的捕捉
  • 如何将 ONLYOFFICE 文档集成到使用 Laravel 框架编写的 PHP 网络应用程序中
  • Nginx/OpenResty HTTP 请求处理阶段与 Lua 实践全解20250717
  • Java 大视界 -- Java 大数据在智能交通智能公交站台乘客流量预测与服务优化中的应用(349)
  • Zabbix 分布式监控系统架构设计与优化
  • iOS 构建配置与 AdHoc 打包说明
  • Spring Boot整合阿里云OSS企业级实践:高可用文件存储解决方案
  • 川翔云电脑:云端算力新标杆,创作自由无边界
  • javax.servlet.http.HttpServletResponse;API导入报错解决方案
  • LeetCode热题100【第二天】
  • Linux 基础学习
  • MySQL安全修改表结构、加索引:ON-Line-DDL工具有哪些