C++类的多种构造函数

news/2024/11/20 23:32:51/

目录

    • 默认构造函数
    • 普通构造函数
    • 拷贝构造函数
      • vector存含有指针的自定义类型主义问题
    • 转换构造函数
    • 移动构造函数
      • 举例两个场景

下面以Complex 复数类来学习C++类中的各种构造函数;

#include <iostream>
using namespace std;//复数类
class Complex{friend ostream & operator<<(ostream &out, Complex &c);  //友元函数
public:Complex(){}  //默认构造函数Complex(double real, double imag): m_real(real), m_imag(imag){ }  //普通构造函数(可以加缺省值)Complex(const Complex &c):m_real(c.m_real),m_imag(c.m_imag) { } //拷贝构造函数(参数是const Complex &类型)Complex(double real):m_real(real), m_imag(0.0) { }  //转换构造函数 Complex(Complex &&c):m_real(c.m_real),m_imag(c.m_imag) { }  //移动构造函数 private:double m_real;  //实部double m_imag;  //虚部
};//重载<<运算符
ostream & operator<<(ostream &out, Complex &c){out << c.m_real <<" + "<< c.m_imag <<"i";;return out;
}int main(){Complex a(10.0, 20.0);  //调用普通构造函数Complex b(89.5);  //调用转换构造函数Complex c;  //调用默认构造函数return 0;}

默认构造函数

  • 未显式提供初始值时,用来创建对象的构造函数;
Complex(){} //默认构造函数

如果该类采用了继承或者定义了虚函数或者成员由非内置类型,那么系统在我们没有定制写出该函数的时候,会自动生成默认构造函数;


普通构造函数

 Complex(double real, double imag);  //普通构造函数(可以带上缺省参数方便初始化!)

用户可以传参与对应普通构造函数进行匹配来构造对象;


拷贝构造函数

拷贝构造 也叫 复制构造

Complex(const Complex &c); //拷贝构造函数(参数是const Complex &类型)

调用场景:

Complex a(1.1,2.2);//用户调用普通构造函数初始化a对象Complex b(a); //此时b调用拷贝构造函数,用a对象进行拷贝 
Complex b = a;  //等价与上面的Complex b(a);//下面两条语句不会调拷贝构造,因为c1,c2已经通过默认构造初始化了,再进行=只是调用了operator=(const Complex& src)赋值语句
Complex c1, c2;
c1=c2;

注意,拷贝构造函数与默认构造函数类似,系统一定条件下会自动生成;

(但是系统默认生成的拷贝构造是浅拷贝,遇到了某些自定义类型是很危险的!eg:string 内部有堆地址char*的浅拷贝问题)
拷贝构造参数是const 类型名 &a 的原因是:

  1. &避免了传参时候,需要多拷贝一次的开销
  2. 不加&会产生“拷贝时需要传参,传参又是一次拷贝的套娃行为”
  3. 用a对象拷贝构造的时候,const防止在函数内部拷贝a时,不小心把a本体给改了;

vector存含有指针的自定义类型主义问题

vectror, 万一存的自定义对象里有含有堆上的指针咋办? 比如存个string,string底层就是个char* ;
这就得对存储的自定义对象设计深拷贝的拷贝构造函数!!!
否则系统自动生成的浅拷贝就会出现内存问题!string里已经帮我们搞了深拷贝的靠背可构造函数了不用担心;


转换构造函数

  • C++ 允许我们自定义类型转换规则,用户可以将其它类型转换为当前类类型。这种自定义的类型转换规则只能以类的成员函数的形式出现,换句话说,这种转换规则只适用于类。

调用场景:

  • 直接一个=,将等号右边的其它类型转换为当前类类型
  • or(类型)强转时调用转换构造函数(有点C语言隐式类型转换的意思)
Complex(double real):m_real(real), m_imag(0.0) { }  //转换构造函数 

调用场景1:

 Complex a(1.1,2.2);//先用普通构造初始化a对象;
cout<<a<<endl; //输出1.1 + 2.2ia = 3.14; //double转Complex,调用转换构造,将double 3.14直接转换为了Complex类型: a(3.14,0.0)  ; 有点像C的隐式类型转换 int a = 3.14这种操作
cout<<a<<endl; //输出3.14 + 0.0i

调用场景2(本质上等价于场景1):

 Complex a = (Complex)25.5;;//C语言的强转;

如果Complex类没有写对应double类型的转换构造函数:Complex(double a),那编译器因为没有转换规则,上述两个场景就会报错,转换失败!


移动构造函数

Complex(Complex &&c) { }  //移动构造函数格式(参数是一个右值 or 将亡值)

先了解一下移动语义: 右值引用和移动语义参考博客

所谓移动语义,指的就是以移动而非深拷贝的方式初始化含有指针成员的类对象

简单的理解,移动语义指的就是将其他对象(通常是临时对象(将亡值))拥有的内存资源“移为已用”。

事实上,对于程序执行过程中产生的临时对象,往往只用于传递数据(没有其它的用处),并且会很快会被销毁。因此在使用临时对象初始化新对象时,我们可以将其包含的指针成员指向的内存资源直接转移给新对象所有无需再新拷贝一份,这大大提高了初始化的执行效率

移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的参数是一个右值引用

当类中同时包含拷贝构造函数和移动构造函数时,如果使用临时对象初始化当前类的对象,编译器会优先调用移动构造函数来完成此操作。只有当类中没有合适的移动构造函数时,编译器才会退而求其次,调用拷贝构造函数。

这意味着,移动构造函数的参数是一个右值或者将亡值的引用;

也就是说,使用一个右值,或者将亡值初始化另一个对象的时候,才会调用移动构造函数

举例两个场景

(eg1:某个成员函数,返回该类型自己的对象的场景) string 的string operator+();:

img

可以看到,有了移动构造以后,我们避免了拷贝临时变量,再释放临时变量的操作,我们直接把临时变量内的指针转移给了要赋值的对象,避免了多余的拷贝,提高了效率!

(eg2:move() 函数强制让一个左值变成右值,因此a=move(b),等号右边是右值,那么调用的就是移动构造函数:

执行过程:

  1. move让b变成了右值,走了移动构造;,把b中的各种资源转移给了a;
  2. b完成转移以后,其中的资源置空nullptr,代表他的生命周期结束了,置空的原因是防止对同一个地址2次析构等;

很明显,移动构造的优势:

  1. 减少了额外深拷贝的时间上的开销;
  2. 不需要给构造的对象分配内存,因为我们把参数(右值)的资源转移过去就行;

http://www.ppmy.cn/news/8639.html

相关文章

【Linux】shell命令以及运行原理和Linux权限的理解

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;&#x1f6b2;Linux &#x1f4d5; 学习格言&#xff1a;博观而约取&#xff0c;厚积而薄发 …

elasticsearch 的基本操作多维度分享

目录 一、索引操作 二、映射操作 三、文档操作 elasticsearch 的基本操作多维度分享此篇正式分享&#xff0c;具体包括索引、映射、文档的相关处理&#xff0c;模拟生成环境&#xff0c;通过DSL语句和java的高级REST形式全方位展示给大家&#xff1b; 一、索引操作 1、创建…

密码技术扫盲,Part 3:认证

个人博客 密码技术扫盲&#xff0c;Part 1&#xff1a;对称加密密码技术扫盲&#xff0c;Part 2&#xff1a;非对称加密&#x1f3af; 密码技术扫盲&#xff0c;Part 3&#xff1a;认证 除了加密&#xff0c;还有一类用法是对信息的认证&#xff0c;主要包括 4 个技术 单向散…

如何在星巴克连接家中Windows台式机?(安卓,iOS, Windows, macOS配合frp公网iP实现)...

zhaoolee 最近热衷于和海外热心老哥们交换硬盘中的单机游戏资源(BT下载)&#xff0c;家中有Windows台式机&#xff0c; 适合长时间挂机下载BT资源&#xff0c;zhaoolee希望能随时连接到Windows台式机新增下载任务&#xff0c;安装体积超大的主机游戏。 另外&#xff0c;公司有一…

Maven 之 依赖管理

目录 1、依赖传递 小案例&#xff1a; 2、可选依赖 3、 排除依赖 4、可选依赖和排除依赖的区别 我们开发一个工程需要用到大量得jar包&#xff0c;而这些jar 包就是我们所说得依赖&#xff0c;一个项目可以配置多个依赖。 1、依赖传递 我们来看一下今天用来演示的工程。…

微信小程序介绍

目录 1.什么是小程序&#xff1f; 2.小程序可以干什么&#xff1f; 2&#xff0c;1.相关资料 2.2.申请微信小程序测试账号 3. 开发一个demo 3.1 创建项目 3.2 配置 3.3 常用框架 3.4 目录结构说明 目录结构 小程序代码构成 JSON 配置 小程序配置 app.json 工具配…

Linux权限及其理解

文章目录&#xff1a;Linux权限的概念Linux权限管理文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性&#xff09;文件权限值的表示方法文件访问权限的设置方法权限掩码目录的权限粘滞位总结Linux权限的概念 与其它系统相比&#xff0c;Lin…

ZC706P+ADRV9009连接RADIOVERSE详解之三

做好SD卡映像&#xff0c;连接好硬件之后&#xff0c;我们就可以尝试软件操作了。 步骤1&#xff1a;设置好网络 打开软件界面我们看到&#xff0c;板子默认的地址为192.168.1.10 端口号为55555.我们一定也设置跟板子连接的以太网口处于192.168.1网段&#xff0c;并且子网掩码…