【C\C++学习】之十八、C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)

2023-11-18

在C++中,有三大函数复制控制(复制构造函数,赋值操作符,析构函数),而在C++11中,加入了移动构造函数,移动赋值操作符。我就斗胆将他们命名为六大函数好了。

一、构造函数

c++primer中说过:构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。构造函数的工作就是保证每个对象的数据成员具有合适的初始值。

构造函数与其他函数不同:构造函数和类同名,没有返回类型。

构造函数与其他函数相同:构造函数也有形参表(可为void)和函数体。  (参数表为void的构造函数为默认构造函数)

构造函数构造类对象的顺序是:1.内存分配,构造函数调用的时候 隐士\显示的初始化各数据。

2.执行构造函数的运行。

1、构造函数初始化表

A() :a(0){}
我们使用构造函数初始化表示初始化数据成员,然而在没有使用初始化表的构造函数则在构造函数体中对数据成员赋值。

在我们编写类的时候,有些成员必须在构造函数初始化表中进行初始化。(没有默认构造函数的类类型成员,const或者引用类型成员)

在编写代码的时候,要注意的是:可以初始化const对象或者引用类型的对象,但不能对他们进行赋值。 也就是需要在我们执行构造函数函数体之前完成初始化工作,所以唯一的机会就是初始化表。从这一点可以看出初始化表的执行先于函数体。

在初始化表中,成员被初始化的次序不是你编写初始化表的次序,而是定义成员的次序。


初始化列表在初始化类类型的成员时,要指定实参并传递给成员类型的一个构造函数。

在c++primer中有一个书店的例子:

Sales-item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}


我们的初始化表在什么时候必须使用呢 ?

当有一个类成员,他本身就是结构或者类的时候,并且只有一个带参数的构造函数,(无默认构造函数) 此时我们要对成员进行初始化,就需要调用成员的构造函数,此时需要我们的初始化表,如果不使用初始化表,那么内存分配就会出问题。

初始化列表的优点:主要是对于自定义类型,初始化列表是作用在函数体之前,他调用构造函数对对象进行初始化。

然而在函数体内,需要先调用构造函数,然后进行赋值,这样效率就不如初始化表。



2、默认实参构造函数

A(int i = 1) :a(i), ca(i), ra(i){}


3、默认构造函数

合成的默认构造函数:当类中没有定义构造函数(注意是构造函数)的时候,编译器自动生成的函数。

但是我们不能过分依赖编译器,如果我们的类中有复合类型或者自定义类型成员,我们需要自己定义构造函数。

自定义的默认构造函数:

A(): a(0) {}
A(int i = 1): a(i) {}
可能疑问的是第二个构造函数也是默认构造函数么?是的,因为参数中带有默认值。

我们来看一张图,就会一目了然了:


4、类型转换

在C++primer中,书店问题中的一个例子是 传递string对象或者iostream对象到参数中,会发生隐式转换,这样就会出现问题。

explicit关键字可以抑制隐式转换。

explicit Sales_item(const string &book): isbn(book) {}

如果我们声明了构造函数禁止隐式转换, 可以将其他对象显示转换后传入构造函数。

string a = "d";
item.same(Sales_item(a));

二、移动构造函数

在C++11中新加入的特性!

在上一篇blog中我加入了一张图,可以具体看到移动构造函数的运行原理。


此时,我们偷走了临时变量的内存空间,据为己用。节省了开辟空间的时间。

	A(A && h) : a(h.a)
	{
		h.a = nullptr; //还记得nullptr?
	}

可以看到,这个构造函数的参数不同,有两个&操作符,   移动构造函数接收的是“右值引用”的参数。

还要来说一下,这里h.a置为空,如果不这样做,h.a在移动构造函数结束时候执行析构函数会将我们偷来的内存析构掉。h.a会变成悬垂指针

移动构造函数何时触发?  那就是临时对象(右值)。用到临时对象的时候就会执行移动语义。

这里要注意的是,异常发生的情况,要尽量保证移动构造函数 不发生异常,可以通过noexcept关键字,这里可以保证移动构造函数中抛出来的异常会直接调用terminate终止程序。

右值引用:

在上一篇blog中,我们提到过将亡值,他是c++11新增的跟右值引用相关的表达式。

在c++11中,右值引用就是对一个右值进行引用的类型,右值通常不具有名字,我们就只能通过引用的方式找到它的存在了。

比较一下下面两条语句:

T &&a = returna();
T b = returnb();
此时a是右值引用,他比b少了一次对象析构和对象构造的过程。a直接绑定了returna返回的临时变量。b只是由临时变量值构造而成的。

应该可以看清楚了吧。右值引用就是让返回的右值(临时对象)重获新生,延长生命周期。临时对象析构了,但是右值引用存活。

不过要注意的是,右值引用不能绑定左值:int a; int &&c = a;   这样是不行的。


这里有一个函数就是 move函数,它能够将左值强制转换成右值引用。


三、移动赋值操作符

他的原理跟移动构造函数相同,这里不再多说。

给出实现代码:

	A & operator = (A&& h)
	{
		assert(this != &h);

		a = nullptr;
		a = move(h.a);
		h.a = nullptr;
		return *this;
	}



复制控制

四、复制构造函数

他是一种特殊的构造函数,具有单个形参,形参是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。当将该类型的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造函数。

必须定义复制构造函数的情况:

1.、类有一个或者多个数据成员是指针。

2、有成员表示在构造函数中分配的其他资源。另外的类在创建新对象时必须做一些特定的工作。

下面给出赋值构造函数的编写:

A(const A& h) :a(h.a){}
如果不想让对象复制呢? 那就将复制构造函数声明为:private;

五、赋值操作符

他跟构造函数一样,赋值操作符可以通过制定不同类型的右操作数而重载。

赋值和复制经常是一起使用的,这个要注意。

下面给出赋值操作符的写法:

	A& operator = (const A& h)
	{
		assert(this != &h);

		this->a = h.a;

		return *this;
	}


六、析构函数

是构造函数的互补,当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。析构函数可用于释放对象时构造或在对象的生命期中所获取的资源。不管类是否定义了自己的析构函数,编译器都会自动执行类中非static数据成员的析构函数。

析构函数的运行:

当对象引用或指针越界的时候不会执行析构函数,只有在删除指向动态分配对象的指针或实际对象超出作用域时才会调用析构函数。

合成析构函数:

编译器总是会合成一个析构函数,合成析构函数按对象创建时的逆序撤销每个非static成员。要注意的是,合成的析构函数不会删除指针成员所指向的对象。


最后要注意的是:类如果需要析构函数,那么他肯定也需要复制构造函数和赋值操作符。



blog的最后给出完整的六大函数的代码。

#include <iostream>
#include <assert.h>
using namespace std;

class Temp
{
public:
	Temp(const char* str = nullptr);
	Temp(Temp&& t);
	Temp& operator = (Temp&& t);
	Temp(const Temp& t);
	Temp& operator = (Temp& t);
	~Temp(void);
private:
	char *m_pData;
};

Temp::Temp(const char* str)
{
	if (!str)
	{
		m_pData = nullptr;
	}
	else
	{
		this->m_pData = new char[strlen(str) + 1];
		strcpy(this->m_pData, str);
	}
}

Temp::Temp(Temp&& t) :m_pData(move(t.m_pData))
{
	t.m_pData = nullptr;
}

Temp& Temp::operator = (Temp&& t)
{
	assert(this != &t);

	this->m_pData = nullptr;
	this->m_pData = move(t.m_pData);
	t.m_pData = nullptr;

	return *this;

}

Temp::Temp(const Temp& t)
{
	if (!t.m_pData)
	{
		this->m_pData = nullptr;
	}
	else
	{
		this->m_pData = new char[strlen(t.m_pData) + 1];
		strcpy(this->m_pData, t.m_pData);
	}
}

Temp& Temp::operator = (Temp &t)
{
	if (this != &t)
	{
		delete[] this->m_pData;
		if (!t.m_pData)
		{
			this->m_pData = nullptr;
		}
		else
		{
			this->m_pData = new char[strlen(t.m_pData) + 1];
			strcpy(this->m_pData, t.m_pData);
		}
	}

	return *this;
}

Temp::~Temp(void)
{
	if (this->m_pData)
	{
		delete[] this->m_pData;
		this->m_pData = nullptr;
	}
}



---2013.12.20

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C\C++学习】之十八、C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数) 的相关文章

随机推荐

  • Linux 搭建 JumpServer 堡垒机

    跳板机概述 跳板机就是一台服务器 开发或运维人员在维护过程中首先要统一登录到这台服务器 然后再登录到目标设备进行维护和操作 跳板机缺点 没有实现对运维人员操作行为的控制和审计 使用跳板机的过程中还是会出现误操作 违规操作导致的事故 一旦出现
  • python下载文件暂停恢复_新技能 get —— Python 断点续传下载文件

    原博文 2017 03 24 12 44 from urllib request import urlretrieve import sys import os prev reported download percent None 首先定
  • Java复习-25-单例设计模式

    单例设计模式 目的 使用场景 在实际开发下 会存在一种情况 某一种类在程序的整个生命周期中 只需要实例化一次就足够了 例如 系统数据类 由于操作系统只有一个 因此在程序初始化时该类只需要实例化一次 之后的系统数据更改都是在这一个实例化对象中
  • sha256是什么算法,sha256的安全性,sha256算法详解

    一些朋友对密码学方面的知识不是很了解 什么sha1 sha256 sha512等等 这么多算法类似的算法可能会有一点混淆 尤其是对sha256不是很理解 本文整合了一些关于sha256的相关资料 针对sha256是什么算法 它的安全性如何
  • SSM商城项目实战:订单管理

    SSM商城项目实战 订单管理 在SSM商城项目中 订单管理是一个非常重要的功能模块 本文将详细介绍订单管理的实现思路和步骤代码 实现SSM商城项目中订单管理功能的思路如下 设计数据库表结构 根据订单管理的需求 设计订单表和订单明细表的结构
  • 5种IO流

    在 Unix网络编程 一书中提到了五种IO模型 分别是 阻塞IO 非阻塞IO 多路复用IO 信号驱动IO以及异步IO 下面就分别来介绍一下这5种IO模型的异同 1 阻塞IO模型 最传统的一种IO模型 即在读写数据过程中会发生阻塞现象 当用户
  • QuickHit小游戏代码实现

    首先先明确下这个小游戏的需求 1 根据输入速率和正确率将玩家分为不同级别 2 级别越高 一次显示的字符数越多 玩家正确输入一次的得分也越高 3 规定时间内完成规定次数的输入 正确率达到规定要求 则升级 玩家最高级别为6级 初始级别一律为1级
  • 4年.Net程序员转Java学习笔记(1)为什么要转

    本人在一个普通二线城市 在大学期间为通信工程专业 上课学的C 通过看师兄师姐留下来的视频以及百度自学了C 在学校里做了一些winForm项目 XXX管理系统 很水 BUG多 纯粹是自己做来玩的 界面设计 数据库 后端都是自己设计 毕业工作后
  • 层叠上下文-《CSS 世界》阅读笔记

    层叠规则 目录 层叠规则 目录 z index 什么是层叠上下文 什么是层叠水平 层叠顺序 层叠准则 层叠上下文的特性 笔者个人理解 层叠上下文的创建 定位元素与传统层叠上下文 层叠上下文与层叠顺序 z index 负值深入理解 z ind
  • Ajax-发送请求的五个步骤

    概述 Asynchronous Javascript And XML AJAX 不是一门的新的语言 而是对现有技术的综合利用 ajax 是一种技术方案 依赖浏览器提供的 XMLHttpRequest 对象发出 HTTP 请求和接收 HTTP
  • python学习笔记(1)之如何在python3.x下安装PIL、numpy以及matplotlib

    本人是大菜鸟一枚 也是第一次动手写博客 希望能够通过写博客总结自己学到的知识 同时得到与大牛们交流的机会 本人所写的内容仅是自己的一点学习总结 若有不对的地方 欢迎大家的批评指正 安装python3 5 2 python3 5 x的下载路径
  • Chain of Responsibility:责任链模式

    现有多个处理者 这些处理者可以处理不同的请求 这些处理者有等级关系 每个处理者都有更上级的处理者 对于最上级的处理者 可以没有上级 对于一个上级处理者 可以有多个下级处理者 客户端发出了一个请求 请求被传递给一个最下级处理者 而该处理者无法
  • Spring框架的前世今生与系统架构

    课题 Spring框架的前世今生及系统概述 课程目标 1 通过对本章内容的学习 可以掌握Spring的基本架构及各子模块之间的依赖关系 2 了解Spirng的发展历史 启发思维 3 对Spring形成一个整体的认识 为之后的深入学习做铺垫
  • 资讯汇总230207

    230207 14 50 中国银河 硅料价格深V反弹 反映出需求回暖 中国银河研报称 春节后硅料价格快速回调 需求回暖支撑硅料上行 但硅料全年供过于求 短期反弹或难维持 政策目标明确 光伏高景气度将持续 随着硅料进入下行周期 光伏产业链利润
  • 彻底消除wine中文乱码

    wine下中文的配置方案 步骤 1 初始设置 运行 winecfg 把模拟的 Windows 系统设置为 Windows XP 或者 Windows 2000 不过亲测非必须 2 准备字体 为了让 Windows 应用程序看上去更美观 所以
  • mysql的checkpoint

    上一章的结尾我们留下了一个问题 就是在上一章所介绍的模型中 恢复管理器必须要通过全篇扫描整个undolog进行日志恢复 这样做显然是没有太大必要的 因为系统中断肯定是在最后几个事务受到影响 前面的事务应该已经完成commit或者rollba
  • 漏洞预警:Tomcat爆出安全绕过漏洞CVE-2018-1305 789多版本受影响

    Apache Tomcat爆出安全绕过漏洞 CVE编号CVE 2018 1305 Apache Tomcat 7 8 9多个版本受到影响 攻击者可以利用这个问题 绕过某些安全限制来执行未经授权的操作 这可能有助于进一步攻击 漏洞概述 在 A
  • 在react中如何结合antd实现分页功能

    在react中如何结合antd实现分页功能 步骤如下 1安装antd npm i antd s 2在 src App css中引入 antd dist antd css import antd dist antd css 3在需要用分页器的
  • Global illumination(全局光照或者全局照明) 概念

    Global illumination 全局光照或者全局照明 前言 在现实世界中 光能从一个曲面反弹到另一个曲面 这往往会使阴影变得柔和 并使照明比不反弹光能时更加均匀 但在 3D 图形学中 默认情况下光线不反弹 必须使程序生成反弹照明的模
  • 【C\C++学习】之十八、C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)

    在C 中 有三大函数复制控制 复制构造函数 赋值操作符 析构函数 而在C 11中 加入了移动构造函数 移动赋值操作符 我就斗胆将他们命名为六大函数好了 一 构造函数 c primer中说过 构造函数是特殊的成员函数 只要创建类类型的新对象