当前位置: 首页 > 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.dtcms.com/a/285295.html

相关文章:

  • 鸿蒙UI自动化测试框架Hypium的使用指南
  • Netty入门学习
  • java-字符串和集合
  • (自用)补充说明2
  • 《python语言程序设计》2018版第8章8题编写函数实现二进制转十进制(字符串变整数)!!整数没法进行下标
  • 基于MATLAB的极限学习机ELM的数据回归预测方法应用
  • 用python可视化分析二次元经济的爆发
  • python网络爬虫(第一步:网络爬虫库、robots.txt规则(防止犯法)、查看获取网页源代码)
  • 2025年6月GESP(C++一级):假期阅读
  • Baumer工业相机堡盟工业相机如何通过YoloV8模型实现人物识别(C#)
  • 2025年6月GESP(C++一级):值日
  • VOTE:基于轨迹集成投票的视觉-语言-动作模型优化
  • 掌握工程化固件烧录,开启你的技术进阶之路-STM32系列
  • C#将类属性保存到Ini文件方法(利用拓展方法,反射方式获取到分组名和属性名称属性值)
  • vue-cli 模式下安装 uni-ui
  • 2-Vue3应用介绍
  • STM32f103ZET6之ESP8266模块
  • AlpineLinux的内核优化
  • AI搜索+GEO时代的营销策略更迭学习笔记
  • 计算机的网络体系及协议模型介绍
  • 【Java】文件编辑器
  • ROS1/Linux——Launch文件使用
  • 【深度学习新浪潮】AI在finTech领域有哪些值得关注的进展?
  • STM32 IAR 生成工程后配置
  • CSP-S模拟赛三(仍然是难度远超CSP-S)
  • 【Java源码阅读系列55】深度解读Java Method 类源码
  • 78、【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:栈指针和帧指针(下)
  • 股票行情接口api,板块、概念接口,股票主力资金流接口,板块概念资金流接口
  • 暑期自学嵌入式——Day05(C语言阶段)
  • 1-创建Vue3项目