C++11——新的类功能与可变参数模板

系列文章目录


文章目录

  • 系列文章目录
  • 一、新的类功能
      • 默认成员函数
      • 类成员变量初始化
      • 强制生成默认函数的关键字default
      • 禁止生成默认函数的关键字delete
      • 继承和多态中的final与override关键字
  • 二、可变参数模板
      • 递归函数方式展开参数包
      • 逗号表达式展开参数包
      • STL容器中的empalce_back与push_back的区别


一、新的类功能

默认成员函数

原来C++类中,有6个默认成员函数:

  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值重载
  5. 取地址重载
  6. const 取地址重载

最后重要的是前4个,后两个用处不大,默认成员函数就是我们不写编译器会生成一个默认的

C++11 新增了两个:移动构造函数和移动赋值运算符重载

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

  1. 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造
  2. 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
  3. 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

我们用之前自实现的string类来观察上述规律

namespace Tlzns
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 移动构造string(string&& s){swap(s);cout << "string(string&& s) -- 移动构造" << endl;}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}// 移动赋值string& operator=(string&& s){swap(s);cout << "string& operator=(string&& s) -- 移动赋值" << endl;return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;};
}
class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p):_name(p._name), _age(p._age){}Person& operator=(const Person& p){if (this != &p){_name = p._name;_age = p._age;}return *this;}~Person(){}
private:Tlzns::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);Person s4;s4 = std::move(s2);return 0;
}

在Person类没有实现移动构造函数,且实现析构函数 、拷贝构造、拷贝赋值重载的情况下
注:自实现的string类中已经支持移动构造和移动赋值重载
在这里插入图片描述
我们将Person类中实现的析构函数 、拷贝构造、拷贝赋值重载都注释掉后

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}/*Person(const Person& p):_name(p._name), _age(p._age){}Person& operator=(const Person& p){if (this != &p){_name = p._name;_age = p._age;}return *this;}~Person(){}*/
private:Tlzns::string _name;int _age;
};

在这里插入图片描述
可以观察到编译器默认生成了移动构造和移动赋值重载

类成员变量初始化

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化

class A
{
public:~A() {}
private:int _a = 1;// 缺省值Tlzns::string _s = "aaa";// 缺省值
};

这里的缺省值会在构造/拷贝构造的初始化列表使用:
如果在构造/拷贝构造有这两个的初始化就不会走缺省值,谁不在初始化列表初始化就走谁的缺省值,相当于是一层额外的保险,如果初始化列表没有,就会走这里获得缺省值

强制生成默认函数的关键字default

C++11可以让你更好的控制要使用的默认函数,假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成
比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p):_name(p._name), _age(p._age){}Person& operator=(const Person& p){if (this != &p){_name = p._name;_age = p._age;}return *this;}~Person(){}Person(Person&& p) = default;Person& operator= (Person&& p) = default;private:Tlzns::string _name;int _age;
};

将移动拷贝和移动赋值重载函数加上default,这样即使实现了析构函数 、拷贝构造、拷贝赋值重载,编译器也会默认生成
在这里插入图片描述

禁止生成默认函数的关键字delete

如果能想要限制某些默认函数的生成
在C++98中,是该函数设置成private,并且只声明不定义,这样只要其他人想要调用就会报错
在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数

继承和多态中的final与override关键字

继承和多态中的内容
在C++中,final关键字可以用于类和成员函数。当用于类时,它表示该类不能被继承(Inheritance)。当用于成员函数时,它表示该成员函数不能在子类中被覆盖(Overriding)

如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译

二、可变参数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数

递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value << " ";ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

逗号表达式展开参数包

template <class T>
void PrintArg(T t)
{cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... };cout << endl;
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数,这种就地展开参数包的方式实现的关键是逗号表达式,我们知道逗号表达式会按顺序执行逗号前面的表达式

expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组,{(printarg(args), 0)…}将会展开成((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc… ),最终会创建一个元素值都为0的数组int arr[sizeof…(Args)],由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包

STL容器中的empalce_back与push_back的区别

template <class... Args>
void emplace_back (Args&&... args);

我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用

而有这样的设计的empalce_back与push_back的区别在哪呢

  1. 构造函数的使用:
    emplace_back 可以直接将构造函数所需的参数传递过去,然后构建一个新的对象并将其填充到容器的尾部,它不会立即拷贝或移动新对象的副本,而是直接在容器尾部创建该对象,只调用了一次构造函数。push_back 首先会利用传入的参数调用构造函数构造一个临时的对象,然后将这个临时对象拷贝或移动到容器中,这个过程涉及到拷贝构造函数的使用,可能会导致额外的性能开销
  2. 效率问题:
    使用 emplace_back 可以更好地避免内存的拷贝和移动,从而提升容器插入元素的性能,如果容器的大小接近于某个特定的数值(如1, 2, 4, 8等),每次扩容都需要从头开始复制所有元素,这可能导致效率降低。在这种情况下,emplace_back 由于是在容器末尾直接创建新对象,因此不会有这样的问题
  3. 支持的功能:
    emplace_back 可以接受多个构造参数,并且支持原地构造,push_back 支持右值引用,但不支持传入多个构造参数,且总是会进行拷贝构造

综上所述,emplace_back 在效率和某些功能上优于 push_back,尤其是在处理大型容器时,因为它避免了不必要的拷贝和移动操作


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

100天精通鸿蒙从入门到跳槽——第20天:ArkTS装饰器@Link双向数据绑定

博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通Golang》 — Go语言学习之旅!《100天精通鸿蒙》 — 从Web/安卓到鸿蒙大师!100天…

免费分享一套微信小程序外卖跑腿点餐(订餐)系统(uni-app+SpringBoot后端+Vue管理端技术实现) ,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序外卖跑腿点餐(订餐)系统(uni-appSpringBoot后端Vue管理端技术实现) &#xff0c;分享下哈。 项目视频演示 【免费】微信小程序外卖跑腿点餐(订餐)系统(uni-appSpringBoot后端Vue管理端技术实现)…

【开源】基于JAVA语言的毕业生追踪系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 登陆注册模块2.2 学生基本配置模块2.3 就业状况模块2.4 学历深造模块2.5 信息汇总分析模块2.6 校友论坛模块 三、系统设计3.1 用例设计3.2 实体设计 四、系统展示五、核心代码5.1 查询我的就业状况5.2 初始化就业状况5.…

SSL加密证书免费申请

首先&#xff0c;让我们来了解一下SSL证书的基本作用。SSL证书通过公钥和私钥的非对称加密技术&#xff0c;使得服务器与浏览器之间的通信内容得到高强度加密&#xff0c;同时验证网站的真实身份&#xff0c;从而提升用户的信任度&#xff0c;也是搜索引擎排名优化的一个重要因…

Idea设置代理后无法clone git项目

背景 对于我们程序员来说&#xff0c;经常上github找项目、找资料是必不可少的&#xff0c;但是一些原因&#xff0c;我们访问的时候速度特别的慢&#xff0c;需要有个代理&#xff0c;才能正常的访问。 今天碰到个问题&#xff0c;使用idea工具 clone项目&#xff0c;速度特…

2.【Vue3】Vue 基本使用——局部使用Vue

文章目录 1. 快速入门2. 常用指令2.1 v-for2.2 v-bind2.3 v-if 与 v-show2.4 v-on2.5 v-model 3. 生命周期4. Ajax 函数库 Axios4.1 Axios 基本使用4.2 Axios 请求方式别名 1. 快速入门 现在需要将 “hello vue3” 这样一个字符串渲染到页面上进行展示。 这个需求并不陌生&…

Vue-Cli3 - 从安装 nodejs 配置环境 ~ 搭建 cli 脚手架项目全过程

目录 前言提示 一、安装 & 配置 nodejs 1.1、安装 nodejs 1.2、配置必要目录 1.3、配置环境变量 1.4、测试 安装&配置 是否成功 1.5、安装淘宝镜像 1.5、cnpm 安装&#xff08;推荐安装&#xff09; 二、vue-cli3 创建项目 2.1、vue-cli2 和 vue-cli3 主要区…

C语言——N / 自定义类型:联合和枚举

目录 一、联合体 1、联合体类型的声明 2、联合体的特点 3、相同成员的结构体和联合体对比 4、联合体大小的计算 5、联合的一个练习 二、枚举类型 1、枚举类型的声明 2、枚举类型的优点 3、枚举类型的使用 一、联合体 1、联合体类型的声明 像结构体⼀样&#xff0c;…

批量导出域控用户及其所在OU和组

在Windows域环境中&#xff0c;批量导出域控用户及其所在OU&#xff08;组织单位&#xff09;和组成员身份信息&#xff0c;可以使用PowerShell脚本实现。以下是一个基本的示例脚本&#xff1a; Import-Module ActiveDirectory# 遍历所有用户 Get-ADUser -Filter * -Propertie…

GitLab16.8配置webhooks、Jenkins2.4配置GitLab插件实现持续集成、配置宝塔面板实现持续部署(其三)

看本篇文章的前提是已经部署完GItlab和Jenkins服务器&#xff0c;已经可以手动构建成功&#xff0c;并且经过了很多次实践&#xff0c;对这两款软件基本熟悉。 建议大家按以下顺序看 前端自动化&#xff08;其一&#xff09;部署gitlab 前端自动化&#xff08;其二&#xff0…

05.领域驱动设计:认识领域事件,解耦微服务的关键

目录 1、概述 2、领域事件 2.1 如何识别领域事件 1.微服务内的领域事件 2.微服务之间的领域事件 3、领域事件总体架构 3.1 事件构建和发布 3.2 事件数据持久化 3.3 事件总线 (EventBus) 3.4 消息中间件 3.5 事件接收和处理 4、案例 5、总结 1、概述 在事件风暴&a…

百川智能发布超千亿大模型Baichuan 3

1月29日&#xff0c;百川智能发布超千亿参数的大语言模型Baichuan 3。在多个权威通用能力评测如CMMLU、GAOKAO和AGI-Eval中&#xff0c;Baichuan 3都展现了出色的能力&#xff0c;尤其在中文任务上更是超越了GPT-4。而在数学和代码专项评测如MATH、HumanEval和MBPP中同样表现出…

【Delphi】系统菜单中增加菜单项

目录 一、问题提出 二、程序截图 ​编辑 ​编辑 三、程序代码&#xff1a; 一、问题提出 我们在开发windows程序的时候&#xff0c;可能会希望在窗体的系统菜单中增加一个菜单项&#xff0c;那么如何实现呢&#xff0c;实际上通过调用windows API是可以实现的&#xff0c;…

go学习之air库的使用

首先下载air库 go install github.com/cosmtrek/air之后你需要去找到库下载的地方&#xff0c;若使用的是go mod可以使用命令 go env GOPATH找到下载库的位置 进入后&#xff0c;有bin&#xff0c;pkg目录&#xff0c;进入bin目录&#xff0c;你能看到air.exe文件 这时候将此…

备战蓝桥杯---二分(入门)

话不多说&#xff0c;先来个模板题来回顾一下上次讲的&#xff1a; 下面是AC代码&#xff1a; 下面进入正题&#xff1a; 本题对1&#xff0c;2行与3&#xff0c;4行组合&#xff0c;再用二分查找即可实现n^2logn的复杂度。 下面是AC代码&#xff1a; 接题&#xff1a; 让我们…

【更新】中国各省市是否属于“宽带中国”试点及“千兆城市”DID数据(2010-2023)

一、数据介绍 数据名称&#xff1a;【更新】中国各省市是否属于“宽带中国”试点及“千兆城市”DID数据 数据范围&#xff1a;全国所有地市 数据年份&#xff1a;2010-2023年 数据来源&#xff1a; “宽带中国”试点城市&#xff0c;来自工信部和国家发改委在2014年、2015…

幻兽帕鲁个人服务器怎么创建?

成功创建幻兽帕鲁服务器教程分享&#xff0c;阿里云和腾讯云均可以&#xff0c;总花费32元即可获得一台换手帕服务器4核16G配置&#xff0c;32人幻兽帕鲁服务器&#xff0c;阿腾云atengyun.com分享当前头部云厂商的Palworld服务器搭建教程&#xff0c;亲测可以&#xff01; 阿…

【JavaScript基础入门】05 JavaScript基础语法(三)

JavaScript基础语法&#xff08;三&#xff09; 目录 JavaScript基础语法&#xff08;三&#xff09;数组概述数组语法多维数组 操作数组修改数组获取数组长度数组和字符串之间的转换添加和删除数组项 Null 和 Undefined字符串连接字符串字符串转换获取字符串的长度在字符串中查…

代码随想录刷题笔记-Day13

1. 二叉树的层序遍历 102. 二叉树的层序遍历https://leetcode.cn/problems/binary-tree-level-order-traversal/层次遍历依靠队列的先进先出特点实现。 解题思路 层序遍历的本质就是对每一个pop出来的处理节点&#xff0c;处理后把他的左右节点放进去。 对于每一层来说&…

Hutool改变我们的coding方式(四)

Hutool改变我们的coding方式 1、随机工具RandomUtil2、唯一ID工具IdUtil3、身份证工具IdcardUtil4、信息脱敏工具DesensitizedUtil 测试代码地址&#xff1a;https://gitee.com/Augenstern-creator/kuang-study_-hutools 1、随机工具RandomUtil RandomUtil主要针对JDK中Rando…