C++11的更新介绍(新的类功能、可变参数模板)

🪐🪐🪐欢迎来到程序员餐厅💫💫💫

          主厨:邪王真眼

主厨的主页:Chef‘s blog  

所属专栏:c++大冒险

总有光环在陨落,总有新星在闪烁


新的类功能

默认成员函数:

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

  1. 构造函数
  2. 析构函数
  3.  拷贝构造函数
  4. 拷贝赋值重载
  5. 取地址重载
  6. const 取地址重载
没印象的朋友可以回顾一下往期内容:
C++6个默认构造函数及const函数

  • C++11新增移动构造函数和移动赋值运算符重载

默认移动构造和默认移动赋值的拷贝方式

  1. 默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
  2. 默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。

移动构造函数和移动赋值运算符重载生成条件

  1. 如果没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。
  2. 如果没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。

 为什么设计这样的条件: 

当涉及深拷贝时,我们 从我们之前实现的移动拷贝可以得知,对于深拷贝的移动构造,是所包含的资源转移要有程序员决定,

即:深拷贝的移动构造要由程序员决定,浅拷贝不用,所以通过上面的条件确定当前类是深拷贝还是浅拷贝


类成员变量初始化

C++11 允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化,这
个我们在雷和对象默认就讲了,这里就不再细讲了,直接上代码。
class A
{int a = 0;//直接给缺省值,如果在构造函数没有处理a的值,那他就是我们所给的缺省值static int b;//静态成员变量不能给缺省值string s = "abc";//自定义类型也可以给缺省值
};

强制生成默认函数的关键字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(Person&& p) = default;
private:bit::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}

注意: 默认成员函数都可以用default关键字强制生成,包括移动构造和移动赋值。


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

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁
已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即
可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p) = delete;
private:bit::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}

final与override关键字

  • final修饰虚函数,表示该虚函数不能再被重写

class Person
{virtual int number()final{return 0;}
};
class Student :public Person
{virtual int number(){return 0;}
};
  • final修饰类,该类不能被继承

class Person final
{int a=0;
};
class Student :public Person
{};
  • override

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

派生类中由于fun1没有const修饰,导致没有实现虚函数重写,此时编译器会报错

class Base
{
public:virtual void fun1() const{;}};class Derived : public Base
{
public:virtual void fun1() override{;}};



可变参数模板

可变参函数模板定义:

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板。
下面就是一个基本可变参数的函数模板
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数
包”,它里面包含了0到N(N>=0)个模版参数。
模板参数包Args和函数形参参数包args的名字可以任意指定,并不是说必须叫做Args和args,判断是否为参数包的主要关键在【省略号】

可变参函数模板使用:

template<class ...Args>
void  A(Args ...args)
{;
}
int main()
{A(0);A('a');A("aa", 'a', 0);
}

但是,我们如何解析参数包中的内容呢?

我们无法直接获取参数包args中的每个参数的, 只能通过展开参数包的方式来获取参数包中的每个参数。但语法不支持使用args[i]这样方式获取可变参数

template<class ...Args>
void ShowList(Args... args)
{for (int i = 0; i < sizeof...(args); i++){cout << args[i] << " ";}cout << endl;
}

递归函数方式展开参数包

展开函数

我们每调一次ShowList函数,就会把第一个参数T val打印,然后把args参数包传给下一次调用的ShowList,接着参数包里的第一个参数会被下一个函数的T类型接受,剩下的会被args接受,如此递归下去,每次剥离出参数包中的一个参数,直到参数包中的所有参数都被取出来。

template<class T,class ...Args>
void ShowList(T val, Args ...args)
{cout << val << endl;ShowList(args);
}

 递归结束:

根据函数匹配原则我们设计一个函数模板,只有一个参数,这样当参数报只剩一个参数时就会跳出循环进入该函数,从而结束递归

template<class T>
void ShowList(T val)
{cout << val << endl;
}

 逗号表达式展开参数包

这种方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。逗号表达式会按顺序执行逗号前面的表达式。
expand函数中的逗号表达式先执行 printarg(args),再得到逗号表达式的结果0。{(printarg(args), 0)...}将会展开成((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... ),最终会创建一个元素值都为0的数组。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args) 打印出参数,也就是说在构造int数组的过程中就将参数包展开了
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;
}

可变参数模板的应用:emplace系列函数

emplace系列的接口,支持模板的可变参数,并且万能引用。
  • emplace与insert对比

我们先分析下面的代码:
对于emplace_back:参数为10和‘a’,根据可变参数模板的性质,他们会直接作为参数传入到list的函数中,再去构建节点中的pair<>,直接构造
对于push_back:参数50,‘e’,会先构造一个临时变量pair<>,接着调用转移构造去构建节点中的pair<>,先是构造函数,在移动拷贝
int main()
{std::list< std::pair<int, char> > mylist;mylist.emplace_back(10, 'a');mylist.push_back({ 50, 'e' });return 0;
}
即:emplace只拷贝了一次,但push_back先拷贝在移动拷贝
但是由于深拷贝中移动拷贝效率高,所以二者差距不明显
  • emplace系列接口相对insert的优势

emplace系列真正的优势在于浅拷贝的类

因为对于深拷贝的且实现了移动构造的类来说,移动构造代价很小,emplace的优势显现不出来。

浅拷贝效率低,则可以认为emplace_back节省了一次拷贝构造的时间,例如日期类

class Date
{
public://构造Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}Date(Date &d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(Date&d)" << endl;}Date(Date&& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(Date&&d)" << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};
int main()
{list<Date> lt1;lt1.push_back({ 2024,3,30 });cout << "=============================================" << endl;lt1.emplace_back(2024, 3, 30);return 0;
}


总结

  1. emplace接口使用需要直接传入参数包才能体现其价值,因为emplace真正高效的情况是传入参数包的时候,直接通过参数包构造出对象,避免了中途的一次拷贝。

  2. emplace对于深拷贝的且实现了移动构造的类意义不大,因为移动构造的代价很小,emplace系列接口对于浅拷贝的类有可观的效率提升

  3. 使用emplace应该直接传参数包,因为如果传入的是对象(不管有名还是匿名),那么emplace系列接口的效率和insert接口的效率一样(都是调用拷贝构造)


    🥰创作不易,你的支持对我最大的鼓励🥰

    🪐~ 点赞收藏+关注 ~🪐

    e3ff0dedf2ee4b4c89ba24e961db3cf4.gif

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

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

相关文章

蓝桥杯嵌入式(G431)备赛笔记——PWM+LCD+按键

目录 题目要求&#xff08;真题&#xff09;&#xff1a; cubeMX配置&#xff1a; 小试牛刀&#xff1a; Keil代码&#xff1a; 效果演示&#xff1a; 题目要求&#xff08;真题&#xff09;&#xff1a; 使用第十一届第二场真题&#xff0c;练习PWM波部分的代码&#xff0c…

C++——StackQueue

目录 一Stack 1介绍 2接口 3模拟实现 4栈的oj题 二Queue 1介绍 2接口 3模拟实现 三容器适配器 1再谈栈和队列 四优先级队列 1接口 ​编辑 2仿函数 五dequeue的简单介绍 一Stack 1介绍 先来看看库中对栈的介绍&#xff1a; 1. stack是一种容器适配器&#x…

【数据结构与算法】:10道链表经典OJ

目录 1. 移除链表元素2. 反转链表2.1反转指针法2.2 头插法 3. 合并两个有序链表4. 分隔链表5. 环形链表6. 链表的中间节点7. 链表中倒数第K个节点8. 相交链表9. 环形链表的约瑟夫问题10. 链表的回文结构 1. 移除链表元素 思路1&#xff1a;遍历原链表&#xff0c;将 val 所在的…

JavaScript函数式编程

函数式编程 课程介绍 为什么要学习函数编程以及什么是函数式编程函数式编程的特性(纯函数、柯里化、函数组合等)函数式编程的应用场景函数式编程库Lodash 为什么要学习函数式编程 函数式编程是非常古老的一个概念&#xff0c;早于第一台计算机的诞生&#xff0c; 函数式编程…

开源模型应用落地-chatglm3-6b-zero/one/few-shot-入门篇(五)

一、前言 Zero-Shot、One-Shot和Few-Shot是机器学习领域中重要的概念&#xff0c;特别是在自然语言处理和计算机视觉领域。通过Zero-Shot、One-Shot和Few-Shot学习&#xff0c;模型可以更好地处理未知的情况和新任务&#xff0c;减少对大量标注数据的依赖&#xff0c;提高模型的…

心理测评性格测试矩阵版h5微信抖音QQ快手小程序app开源版开发

心理测评性格测试矩阵版h5微信抖音QQ快手小程序app开源版开发 支持SAAS、支持独立加密、支持独立开源、价格不同。 自带题库数据&#xff0c;后台一键初始&#xff0c;支持自己上传题目 心理测评 微信公众号微信小程序抖音小程序可打包APP 支持单题、跳跃题、计分题、因子题、…

OSPF数据报文格式

OSPF协议是跨层封装的协议&#xff0c;跨四层封装&#xff0c;直接将应用层的数据封装在网络层协议后面&#xff0c;IP协议包中协议号字段对应的数值为——89 OSPF的头部信息&#xff1a; ——所有数据包公有的信息 版本&#xff1a;OSPF版本 在IPV4中一般使用OSPFV2&#xf…

第十三届蓝桥杯真题:x进制减法,数组切分,gcd,青蛙过河

目录 x进制减法 数组切分 gcd 青蛙过河 x进制减法 其实就是一道观察规律的题。你发现如果a这个位置上的数x&#xff0c;b这个位置上的数是y&#xff0c;那么此位置至少是max(x,y)1进制。一定要把位置找对啊 #include <bits/stdc.h> using namespace std; typedef l…

easyui combobox下拉框组件输入检索全模糊查询

前引&#xff1a; easyui下拉组件&#xff08;combobox&#xff09;&#xff0c;输入检索下拉内容&#xff0c;是默认的右模糊匹配&#xff0c;而且不支持选择。因业务要求需要做成全模糊查询&#xff0c;目前网上搜索有两种方案&#xff1a; 1.修改easyui源码&#xff0c;这个…

K8S node节点配置

1.开始操作之前要先关闭防火墙&#xff0c;SELinux&#xff0c;swap分区 关闭防火墙 sudo systemctl stop firewalld关闭SELinux sudo setenforce 0 # 临时关闭 sudo sed -i s/^SELINUXenforcing$/SELINUXper…

java快速幂算法

快速幂算法 参考视频(参考五角七边up大佬&#xff09; 幂运算的介绍 幂运算是指将一个数自身乘以自身多次的运算&#xff0c;其表达式为 a n a^n an&#xff0c;其中 a a a 是底数&#xff0c; n n n 是指数。 快速幂解释 快速幂算法是一种用于快速计算幂运算的算法&…

可视化后台管理系统-空框架

1.下载element-plus npm install element-plus --save 注意&#xff1a;element-ui不适配vue3&#xff0c;官方已将vue3版本的更新为element-plus 2.main.js配置 // 全局样式 import ./assets/main.cssimport { createApp } from vue import { createPinia } from piniaimpo…

springboot在使用 Servlet API中提供的javax.servlet.Filter 过滤器 对请求参数 和 响应参数 进行获取并记录日志方案

不多说 直接上代码 第一步 package com.xxx.init.webFilter;import com.alibaba.fastjson.JSONObject; import com.xxx.api.constant.CommonConstant; import com.xxx.api.entities.log.OperationLog; import com.xxx.init.utils.JwtHelper; import com.xxx.init.utils.Reques…

【数据结构】07查找

查找 1. 基本概念2. 顺序表查找2.1 顺序查找2.2 顺序查找优化-哨兵 3. 有序表查找3.1 折半查找&#xff08;二分查找&#xff09; 4. 分块查找&#xff08;索引顺序查找&#xff09;5. Hash表&#xff08;散列表&#xff09;5.1 散列函数的设计5.2 代码实现5.2.1 初始化Hash表5…

个人简历主页搭建系列-06:jqcv 简历主题安装

jqcv 介绍 大家好呀&#xff0c;前段时间我在忙毕设的事情&#xff0c;这段时间继续写这个专题。 我们之前网站已经成功搭建起来了对吧&#xff0c;但是这个样式明显和我们的简历需求不符合&#xff0c;难道我们要自己配置 css 文件一点点进行修改吗&#xff1f; 其实并不用…

无人机概述

1、中英文对照表 中文中文简称英文全称英文简称无人驾驶飞机无人机Unmanned Aerial VehicleUAV无人机自组织网络无人机网络flying Ad-Hoc networkFANET 2、相关概念 2.1鲁棒性 网络鲁棒性是指网络系统在面对随机故障、蓄意攻击或其他异常情况时&#xff0c;能够保持其基本功…

记一次http访问超时服务器端调试

问题&#xff1a;http访问服务器时没有返回&#xff0c;没有超时&#xff0c;一直在阻塞 处理过程&#xff1a;telnet端口能连上&#xff0c;服务端程序也不存在处理时间过长的情况。 说明tcp连接没问题。推测是客户端连接后再发起请求&#xff0c;服务端阻塞了。因为很多客户…

C++:类与对象(三)

目录 再谈构造函数 构造函数体赋值 初始化列表 explicit关键字 static成员 友元 友元函数 友元类 内部类 再次理解封装 再谈构造函数 首先要明白声明、定义、初始化三个概念的不同。 声明&#xff1a;指定变量的名字和类型&#xff0c;可以多次声明。 定义&#xf…

c++ 指针总结

概述 内存地址 在计算机内存中&#xff0c;每个存储单元都有一个唯一的地址(内存编号)。通俗理解&#xff0c;内存就是房间&#xff0c;地址就是门牌号 指针和指针变量 指针&#xff08;Pointer&#xff09;是一种特殊的变量类型&#xff0c;它用于存储内存地址。指针的实质…

【Python】面向对象(专版提升2)

面向对象 1. 概述1.1面向过程1.2 面向对象 2. 类和对象2.1 语法2.1.1 定义类2.1.2 实例化对象 2.2 实例成员2.2.1 实例变量2.2.2 实例方法2.2.3 跨类调用 3. 三大特征3.1 封装3.1.1 数据角度3.1.2 行为角度3.1.3 案例:信息管理系统3.1.3.1 需求3.1.3.2 分析3.1.3.3 设计 3.2 继…