【C++:C++11】C++11新特性深度解析:从可变参数模板到Lambda表达式

【C++:C++11】C++11新特性深度解析:从可变参数模板到Lambda表达式

在这里插入图片描述


🎬 个人主页艾莉丝努力练剑
专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:

在这里插入图片描述

🎬 艾莉丝的C++专栏简介:

在这里插入图片描述

文章目录


C++学习阶段的三个参考文档

看库文件(非官方文档):Cplusplus.com

在这里插入图片描述

这个文档在C++98、C++11时候还行,之后就完全没法用了……

准官方文档(同步更新)——还 可以看语法C++准官方参考文档

在这里插入图片描述


这个行,包括C++26都同步了,我们以后主要会看这个。

官方文档(类似论坛):Standard C++

在这里插入图片描述


这个网站上面会有很多大佬,类似于论坛。


在这里插入图片描述

4 ~> 可变参数模版

4.5 emplace系列接口

4.5.1 不同容器emplace系列接口展示

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

4.5.2 浅谈emplace系列接口概念

template<class..· Args>voidemplace_back(Args&&... args);
template<class...Args> iterator emplace(const_iterator position,Args&&...args);

C++11以后STL容器新增了empalce系列的接口,empalce系列的接口均为模板可变参数,功能上兼容push和insert系列,但是empalce还支持新玩法,假设容器为container,empalce还支持直接插入构造T对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造T对象。

emplace_back总体而言是更高效,推荐以后使用emplace系列替代insert和push系列(也不是完全被淘汰了),只是说建议之后用emplace_back替代替代insert和push系列,push_back效率其实也不差,传参数包那种emplace_back效率才有优势,传右值传左值两者效率其实是差不多的,传string参数包有区别——push_back要先移动构造再构造,emplace_back直接构造——一步到位。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如下图,我们模拟实现了list的emplace和emplace_back接口,这里把参数包不段往下传递,最终在结点的构造中直接去匹配容器存储的数据类型T的构造,所以达到了前面说的empalce支持直接插入构造T对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造T对象。

在这里插入图片描述

传递参数包过程中,如果是Args&&... args的参数包,要用完美转发参数包,方式如下std::forward<Args>(args) ...,否则编译时包扩展后右值引用变量表达式就变成了左值。

4.5.3 emplace系列接口在list.h文件中的使用

在这里插入图片描述

4.5.4 emplace系列接口在Test.cpp文件中的使用

在这里插入图片描述

4.5.5 万能引用

在这里插入图片描述
在这里插入图片描述

5 ~> 新的类功能

5.1 默认成员函数:默认移动构造和移动赋值

原来C++类中,有6个默认成员函数:构造函数 / 析构函数 / 拷贝构造函数 / 拷贝赋值重载 / 取地址重载 / const取地址重载,最后重要的是前4个,后两个用处不大(介绍类和对象时也没怎么提),默认成员函数就是我们不写编译器会生成一个默认的。C++11新增了两个默认成员函数:移动构造函数和移动赋值运算符重载

条件苛刻: 如果你没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

在这里插入图片描述

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

在这里插入图片描述

如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

在这里插入图片描述

5.2 成员函数声明时要给缺省值

成员变量声明时给的缺省值(类内成员初始化)会在构造函数的初始化阶段使用。具体来说:如果某个成员变量没有在初始化列表中显式初始化,编译器会自动在初始化列表中使用这个缺省值来初始化它;如果该成员在初始化列表中被显式初始化了,那么显式初始化的值会覆盖声明时的缺省值,这个我们在类和对象部分介绍过了——

往期回顾:【类和对象(下)】C++类与对象的进阶艺术:初始化列表到性能优化的完全指南

在这里插入图片描述

5.3 defult和delete

5.3.1 概念

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

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

5.3.2 最佳实践

如下图所示——

在这里插入图片描述

5.4 目标构造函数和委托构造函数(了解)

5.4.1 目标构造函数

在这里插入图片描述

5.4.2 委托构造函数

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

5.4.3 最佳实践

这两个函数了解即可,艾莉丝给uu们简单演示了一下——

在这里插入图片描述

5.5 final和override

老朋友,这个final和override我们在继承和多态那个章节已经进行了详细讲过了,uu们如果忘了就看一下艾莉丝往期的博客——

【C++:继承】C++面向对象继承全面解析:派生类构造、多继承、菱形虚拟继承与设计模式实践

在这里插入图片描述

【C++:多态】C++多态实现深度剖析:从抽象类约束到虚函数表机制

在这里插入图片描述

这里艾莉丝重新展示一下关于final和override艾莉丝画的思维导图——

在这里插入图片描述

6 ~> C++11:STL的变化

6.1 新的容器

下面这张图中圈起来的就是 C++11的STL中的新增容器,但是实际中最有用的是**unordered_map和unordered_set**。

C++11新增容器:array、forward_list(单链表)、unordered_map和unordered_ed(真正有用的就这俩)——

在这里插入图片描述

这两个我们前面已经进行了非常详细的介绍,其他的大家了解一下即可,艾莉丝这里再把介绍两个容器的博客链接放在这里——

【C++:unordered_set和unordered_map】C++无序容器深度解析:unordered_set和unordered_map的使用

6.2 新的接口

STL中容器的新接口也不少,最重要的就是右值引用和移动语义相关的push / insert / emplace系列接口插入数据系列的接口);移动构造和移动赋值(雪中送炭),还有initializer_list版本的构造(锦上添花的作用)等,这些前面都讲过了,还有一些无关痛痒的cbegin / cend等需要时查查文档即可(文档链接放在开头)。

6.3 宝藏:范围for

容器的范围for遍历,这个在容器部分也讲过了,这里艾莉丝把链接挂在下面了——

【C++:map和set的使用】C++ map/multimap完全指南:从红黑树原理入门到高频算法实战

在这里插入图片描述

7 ~> lambta

7.1 lambta表达式的语法

在这里插入图片描述

7.1.1 概念

lambda表达式本质是一个匿名函数对象,跟普通函数不同的是:lambta表达式可以定义在函数内部

lambda表达式语法使用层而言没有类型,所以我们一般是用auto或者模板参数定义的对象去接收lambda对象

lambda表达式的格式:

[capture-list](parameters)->return type{function boby }

[ capture-list ]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用,捕捉列表可以传值和传引用捕捉,具体细节在下面介绍捕捉列表的部分再细嗦。捕捉列表为空也不能省略。

( parameters ) :参数列表,与普通函数的参数列表功能类似,如果不需要参数传递,则可以连同()一起省略

->returntype:返回值类型,用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。一般返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

{functionboby}:函数体,函数体内的实现跟普通函数完全类似,在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量,函数体为空也不能省略。

在这里插入图片描述

7.1.2 最佳实践

在这里插入图片描述

7.2 lambta的应用场景

7.2.1 说明

在介绍 lambda 表达式之前,我们的使用的可调用对象只有函数指针和仿函数对象,函数指针的类型定义起来比较麻烦,仿函数要定义一个类,相对会比较麻烦。使用lambda去定义可调用对象,既简单又方便。

lambda 在很多其他地方用起来也很好用。比如线程中定义线程的执行函数逻辑,智能指针中定制删除器等, lambda 的应用还是很广泛的,以后我们会不断接触到,主要这个是一个我们之前没有接触过的新知识点,uu们要留意一下哦!

7.2.2 最佳实践

structGoods{ string _name;// 名字double _price;// 价格int _evaluate;// 评价// ...Goods(constchar* str,double price,int evaluate):_name(str),_price(price),_evaluate(evaluate){}};structComparePriceLess{booloperator()(const Goods& gl,const Goods& gr){return gl._price < gr._price;}};structComparePriceGreater{booloperator()(const Goods& gl,const Goods& gr){return gl._price > gr._price;}};structCompareEvaluateGreater{booloperator()(const Goods& gl,const Goods& gr){return gl._evaluate < gr._evaluate;}};structCompareEvaluateLess{booloperator()(const Goods& gl,const Goods& gr){return gl._evaluate < gr._evaluate;}};intmain(){ vector<Goods> v ={{"苹果",2.1,5},{"香蕉",3,4},{"橙子",2.2,3},{"菠萝",1.5,4}};// 类似这样的场景,我们实现仿函数对象或者函数指针支持商品中 // 不同项的比较,相对还是比较麻烦的,那么这里lambda就很好用了 //sort(v.begin(), v.end(), ComparePriceLess());//sort(v.begin(), v.end(), ComparePriceGreater());//sort(v.begin(), v.end(), CompareEvaluateLess());//sort(v.begin(), v.end(), CompareEvaluateGreater());//auto priceLess = [](const Goods& gl, const Goods& gr)// {// return gl._price < gr._price;// };//sort(v.begin(), v.end(), priceLess);sort(v.begin(), v.end(),[](const Goods& gl,const Goods& gr){return gl._price < gr._price;});sort(v.begin(), v.end(),[](const Goods& gl,const Goods& gr){return gl._price > gr._price;});sort(v.begin(), v.end(),[](const Goods& gl,const Goods& gr){return gl._evaluate < gr._evaluate;});sort(v.begin(), v.end(),[](const Goods& gl,const Goods& gr){return gl._evaluate > gr._evaluate;});return0;}

大家应该注意到了被注释掉的代码段其实就是用到了我们的lambda表达式,这四个比较的仿函数,用lambda表达式只要一段代码就能完成,这就是lambda表达式,非常的方便。艾莉丝会在原理部分详细介绍一下——其实lambda原理和同样是C++11更新的内容——范围for——的原理很类似,这里的“很像”不是指lambda的原理也是底层被替换成迭代器(lambda的底层是一个operator()编译器会帮你生成一个仿函数,)这里我们说的“很像”,指的是lambda和范围for都是编译器帮你生成!

7.3 捕捉列表(*)

7.3.1 概念

lambda 表达式中默认只能用 lambda 函数体和参数中的变量,如果想用外层作用域中的变量就需要进行捕捉

第一种捕捉方式 是在捕捉列表中显示的传值捕捉和传引用捕捉,捕捉的多个变量用逗号分割。[x,y,&z]表示x和y是值捕捉,z是引用捕捉。

第二种捕捉方式 是在捕捉列表中隐式捕捉,我们在捕捉列表写一个=表示隐式值捕捉,在捕捉列表写一个&表示隐式引用捕捉,这样我们 lambda 表达式中用了那些变量,编译器就会自动捕捉那些变量。

第三种捕捉方式 是在捕捉列表中混合使用隐式捕捉和显示捕捉。[=,&X表示其他变量隐式值捕捉,x引用捕捉;[&,X,y表示其他变量引用捕捉,x和y值捕捉。当使用混合捕捉时,第一个元素必须是& 或 =,并且&混合捕捉时,后面的捕捉变量必须是值捕捉,同理=混合捕捉时,后面的捕捉变量必须是引用捕捉。

lambda 表达式如果在函数局部域中,他可以捕捉 lambda 位置之前定义的变量,不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉, lambda 表达式中可以直接使用。这也意味着 lambda 表达式如果定义在全局位置,捕捉列表必须为空。

默认情况下, lambda 捕捉列表是被const修饰的,也就是说传值捕捉的过来的对象不能修改,mutable加在参数列表的后面可以取消其常量性,也就说使用该修饰符后,传值捕捉的对象就可以修改了,但是修改还是形参对象,不会影响实参。使用该修饰符后,参数列表不可省略(即使参数为空)。

在这里插入图片描述

7.3.2 最佳实践

在这里插入图片描述


看一下main函数——

在这里插入图片描述

7.4 lambta的原理

7.4.1 原理

lambda的原理和范围for很像,编译后从汇编指令层的角度看,压根就没有lambda和范围for这样的东西。范围for底层是迭代器,而lambda底层是仿函数对象,也就说我们写了一个lambda以后,编译器会生成一个对应的仿函数的类。

仿函数的类名是编译按一定规则生成的,保证不同的lambda生成的类名不同,lambda参数 / 返回类型 / 函数体就是仿函数operator()的参数/返回类型/函数体,lambda的捕捉列表本质是生成的仿函数类的成员变量——也就是说捕捉列表的变量都是lambda类构造函数的实参,当然隐式捕捉不同,编译器也不是傻瓜,实际上,编译器看使用哪些就传哪些对象

7.4.2 最佳实践

我们实践一下——

语法层拿不到lambda的类型,不是说没有类型,而是我们拿不到,但是编译器能够拿到。

简而言之,如下图所示——

在这里插入图片描述

7.4.3 捕捉列表就是仿函数的成员函数

捕捉列表就是仿函数的成员函数——

在这里插入图片描述

7.4.4 补充:成员函数中写了lambda

在这里插入图片描述


也可以修改成员变量,这里this捕捉的本质是lambda可以访问成员变量。

注意:局部的静态变量和全局的全局变量,不用也不能捕捉(两者的生命周期在全局)!


C++11完整代码示例与实践演示

list.h:

#pragmaoncenamespace jqj {// --------------链表节点结构--------------template<classT>structlist_node{ list_node<T>* _next;// 指向下一个节点的指针 list_node<T>* _prev;// 指向前一个节点的指针 T _data;// 节点存储的数据// --------------节点构造函数--------------list_node(const T& x =T()):_next(nullptr),_prev(nullptr),_data(x)// x 节点数据,默认为T类型的默认值{}};// --------------链表迭代器--------------// 实现双向迭代器功能,支持前向和后向遍历template<classT,classRef,classPtr>// T 数据类型// Ref 引用类型(T& 或 const T&)structlist_iterator{// using还具有tepedef没有的功能// 使用类型别名(C++11新特性)using Self = list_iterator<T, Ref, Ptr>;// 自身类型using Node = list_node<T>;// 节点类型 Node* _node;// 当前指向的节点指针// 迭代器构造函数list_iterator(Node* node):_node(node){}// 迭代器解引用操作// *it = 1// Ref 返回节点数据的引用(可读或可写) Ref operator*()// 解引用,Ref就是reference,引用的意思{return _node->_data;}// operator*()返回对应数据类型的引用 Ptr operator->()// 返回对应数据类型的指针{return&_node->_data;}// ++it // 前向迭代操作 Self&operator++()// Self& 返回递增后的迭代器引用{ _node = _node->_next;return*this;} Self operator++(int)// Self 返回递增前的迭代器副本{ Self tmp(*this); _node = _node->_next;return tmp;}// --it// 后向迭代操作 Self&operator--()// Self& 返回递减后的迭代器引用{ _node = _node->_prev;return*this;} Self operator--(int)// Self 返回递减前的迭代器副本{ Self tmp(*this); _node = _node->_prev;return tmp;}// 迭代器比较操作booloperator!=(const Self& s)const// bool 两个迭代器是否不指向同一节点{return _node != s._node;}booloperator==(const Self& s)const// bool 两个迭代器是否不指向同一节点{return _node == s._node;}};//template<class T>//struct list_const_literator//{// using Self = list_const_literator<T>;// using Node = list_node<T>; Node* _node;// Node* _node;// list_const_iterator(Node* node)// :_node(node)// { }// // *it// const T& operator*()// {// return _node->_data;;// }// // ++it// Self& operator++()// {// _node = _node->_next;// return *this;// }// Self operator++(int)// {// Self tmp(*this);// _node = _node->_next;// return *this;// }// // --it// Self& operator--()// {// _node = _node->_prev;// return *this;// }// Self operator--(int)// {// Self tmp(*this);// _node = _node->_prev;// return tmp;// }// bool operator!=(const Self& s) const// {// return _node != s._node;// }// bool operator==(const Self& s) const// {// return _node == s._node;// }//};// --------------链表主体类--------------template<classT>classlist{using Node = list_node<T>;// 节点类型别名public:// 迭代器类型定义 using iterator = list_iterator<T, T&, T*>;// 普通迭代器using const_iterator = list_iterator<T,const T&,const T*>;// 常量迭代器// const T* 只能读数据,不能修改数据//using iterator = list_iterator<T>;//using const_iterator = list_const_iterator<T>;// --------------迭代器访问接口--------------// 获取指向第一个元素的迭代器// iterator 指向首元素的迭代器 iterator begin(){returniterator(_head->_next);}// iterator 指向哨兵节点的迭代器 iterator end(){returniterator(_head);}// 获取指向第一个元素的常量迭代器// const_iterator 指向首元素的常量迭代器 const_iterator begin()const{returnconst_iterator(_head->_next);}// const_iterator 指向哨兵节点的常量迭代器 const_iterator end()const{returnconst_iterator(_head);}// ----------------链表初始化相关----------------- voidempty_init()// 初始化空链表(创建哨兵节点){ _head =new Node; _head->_next = _head; _head->_prev = _head;}// 默认构造函数list(){empty_init();}// 初始化列表构造函数// il 初始化列表list(initializer_list<T> il){empty_init();for(auto& e : il){push_back(e);}}// 范围构造函数// InputIterator 输入迭代器类型template<classInputIterator>list(InputIterator first, InputIterator last)// first 范围起始迭代器 // last 范围结束迭代器{empty_init();while(first != last){push_back(*first);++first;}}// 数量构造函数(size_t版本)list(size_t n, T val =T())// val 元素值,默认为T的默认值{empty_init();for(size_t i =0; i < n;++i){push_back(val);}}// 数量构造函数(int版本)list(int n, T val =T())// val 元素值,默认为T的默认值{empty_init();for(size_t i =0; i < n;++i){push_back(val);}}// -------------析构函数-------------// 清理所有节点并释放哨兵节点~list(){clear();delete _head; _head =nullptr; _size =0;}// -----------拷贝控制函数----------// lt 要拷贝的源链表// ------------传统写法------------// lt2(lt1)list(const list<T>& lt){empty_init();for(auto& e : lt){push_back(e);}}// 拷贝赋值运算符// lt 要拷贝的源链表// list<T>& 返回当前链表的引用// lt1 = lt3 list<T>&operator=(const list<T>& lt){if(this!=&lt){clear();for(auto& e : lt){push_back(e);}}return*this;}////------------现代写法------------//list(list<T>& lt)// list(const list& lt)//{// empty_init();// list tmp(lt.begin(), lt.end());// swap(tmp);//}//// lt1 = lt3////list<T>& operator=(list<T> tmp)//list& operator=(list tmp)//{// swap(tmp);//}// ----------------容量操作---------------// 交换两个链表的内容voidswap(list<T>& lt)// lt 要交换的另一个链表{ std::swap(_head, lt._head); std::swap(_size, lt._size);}// 清空链表中的所有元素// 保留哨兵节点,删除所有数据节点voidclear(){ iterator it =begin();while(it !=end()){ it =erase(it);}}template<class... Args>voidemplace_back(Args&&... args){emplace(end(), args...);emplace(end(), forward<Args>(args)...);}template<class...Args>voidemplace(iterator pos, Args&&...args){ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(forward<Args>(args)...);// prev newnode cur prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode;++_size;}voidpush_back(T&& x){insert(end(), forward<T>(x));}// 在链表头部插入元素,x:要插入的元素值voidpush_front(const T& x){insert(begin(), x);}// 删除链表尾部元素voidpop_back(){erase(--end());}// 删除链表头部元素voidpop_front(){erase(begin());}voidinsert(iterator pos,const T& x)// pos:插入位置的迭代器,x:要插入的元素值{ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(x);// // 连接新节点:prev -> newnode -> cur// prev newnode cur prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode;++_size;}voidinsert(iterator pos, T&& x)// pos:插入位置的迭代器,x:要插入的元素值{ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(move(x));// // 连接新节点:prev -> newnode -> cur// prev newnode cur prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode;++_size;}// 删除指定位置的元素 iterator erase(iterator pos)// iterator 返回指向被删除元素后一个元素的迭代器{ Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next;// 跳过被删除节点:prev -> next prev->_next = next; next->_prev = prev;delete cur;--_size;/*return iterator(next);*/return next;/// 返回下一个节点的迭代器//两种写法都可以}// -------------- 容量信息 ------------------ size_t size()const{//size_t n = 0;//for (auch e : *this)//{// ++n;//}//return n;return _size;}private: Node* _head;// 哨兵头节点指针 size_t _size =0;// 链表元素个数计数器};}

Test.cpp:

#define_CRT_SECURE_NO_WARNINGS1#include<iostream>#include<vector>#include<map>#include<list>#include<string>usingnamespace std;#include<assert.h>#include<algorithm>namespace Alice {classstring{public:typedefchar* iterator;typedefconstchar* const_iterator; iterator begin(){return _str;} iterator end(){return _str + _size;} const_iterator begin()const{return _str;} const_iterator end()const{return _str + _size;}string(constchar* str =""):_size(strlen(str)),_capacity(_size){ cout <<"string(char* str)-构造"<< endl; _str =newchar[_capacity +1];strcpy(_str, str);}voidswap(string& s){ std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){ cout <<"string(char* str)-拷贝构造"<< endl;reserve(s._capacity);for(auto ch : s){push_back(ch);}}// 移动构造string(string&& s){ cout <<"string(char* str)-移动构造"<< endl;swap(s);// 交换后s持有原对象的资源}// s析构时会释放原对象的资源,但原对象现在持有什么? string&operator=(const string& s){ cout <<"string(char* str)-拷贝赋值"<< endl;if(this!=&s){ _str[0]='\0'; _size =0;reserve(s._capacity);for(auto ch : s){push_back(ch);}}return*this;}// 移动赋值 string&operator=(string&& s){ cout <<"string(char* str)-移动赋值"<< endl;swap(s);// 和上面同样的问题return*this;}~string(){//cout << "~string() -- 析构" << endl;delete[] _str; _str =nullptr;}char&operator[](size_t pos){assert(pos < _size);return _str[pos];}voidreserve(size_t new_capacity){//int n = 0; // 触发assert的报错机制if(new_capacity > _capacity){char* tmp =newchar[new_capacity +1];if(_str){strcpy(tmp, _str);// 包括null终止符delete[] _str;} _str = tmp; _capacity = new_capacity;}}voidpush_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;}constchar*c_str()const{return _str;} size_t size()const{return _size;}private:char* _str =nullptr; size_t _size =0; size_t _capacity =0;};// 右值引用和移动语义解决传值返回问题// 传值返回需要拷贝 string addStrings(string num1, string num2){ string str;int end1 = num1.size()-1, end2 = num2.size()-1;// 进位int next =0;while(end1 >=0|| end2 >=0){int val1 = end1 >=0? num1[end1--]-'0':0;int val2 = end2 >=0? num2[end2--]-'0':0;int ret = val1 + val2 + next; next = ret /10; ret = ret %10; str +=('0'+ ret);}if(next ==1) str +='1';reverse(str.begin(), str.end()); cout <<&str << endl;return str;}}//// emplace_back总体而言是更加高效的,推荐以后使用emplace系列替代insert和push系列////int main()//{// list<Alice::string> lt;//// // 传左值,跟push_back一样,走拷贝构造// Alice::string s1("111111111111111111");// lt.emplace_back(s1);// cout << "**************************" << endl;//// // 传右值,跟push_back一样,走移动构造// lt.emplace_back(move(s1));// cout << "**************************" << endl;//// // 直接把构造string参数包往下传,直接用string参数包构造string// // 这里达到的效果是push_back做不到的// lt.push_back("111111111111");// cout << "**************************" << endl;//// lt.emplace_back("111111111111");// cout << "****************************" << endl;//// // 运行结果:// // string(char* str) - 构造// // string(char* str) - 拷贝构造// // **************************// // string(char* str) - 移动构造// // **************************// // string(char* str) - 构造// // string(char* str) - 移动构造// // **************************// // string(char* str) - 构造// // ****************************//// return 0;//}// 日期类structDate{int _y;int _m;int _d;Date(int year,int month,int day):_y(year),_m(month),_d(day){}};//int main()//{// list<pair<Alice::string, int>> lt1;//// // 跟push_back一样// // 构造pair + 拷贝/移动构造pair到list的节点中data上// pair<Alice::string, int> kv("苹果", 1);// lt1.emplace_back(kv);// cout << "****************************" << endl;//// // 跟push_back一样// lt1.emplace_back(move(kv));// cout << "****************************" << endl;//// // 直接把构造pair参数包往下传,直接用pair参数包构造pair// // 这里达到的效果是push_back做不到的// lt1.emplace_back("苹果", 1);// //lt1.push_back("苹果", 1); // 错误,要传pair或者{}隐式转换pair的值// //lt1.push_back({"苹果", 1}); // 要传pair或者{}隐式转换pair的值// cout << "****************************" << endl;//// list<Date> lt;// // 构造 + 拷贝构造// Date d1{ 2025,11,18 };// lt.push_back(d1);//// lt.push_back({ 2025, 11, 18 });//// // 传构造Date的参数,传给形参参数包,参数包往下不断传递,最后直接构造到链表节点上// // 直接构造// lt.emplace_back(2025, 11, 18);//// // 运行结果:// // string(char* str) - 构造// // string(char* str) - 拷贝构造// // ****************************// // string(char* str) - 移动构造// // ****************************// // string(char* str) - 构造// // ****************************//// return 0;//}//#include"list.h"////int main()//{// list<pair<Alice::string, int>> lt1;// cout << "****************************" << endl;//// // 跟push_back一样// // 构造pair + 拷贝/移动构造pair到list的节点中data上// pair<Alice::string, int> kv("苹果", 1);// lt1.emplace_back(kv);// cout << "****************************" << endl;//// // 跟push_back一样// lt1.emplace_back(move(kv));// cout << "****************************" << endl;//// // 直接把构造pair参数包往下传,直接用pair参数包构造pair// // 这里达到的效果是push_back做不到的// lt1.emplace_back("苹果", 1); // 推荐// //lt1.emplace_back({"苹果", 1}); // 错误// //lt1.push_back("苹果", 1); // 错误,要传pair或者{}隐式转换pair的值// //lt1.push_back({"苹果", 1}); // 要传pair或者{}隐式转换pair的值// cout << "****************************" << endl;//// list<Date> lt;// // 构造 + 拷贝构造// Date d1{ 2025,11,18 };// lt.push_back(d1);// lt.push_back({ 2025, 11, 18 });//// // 传构造Date的参数,传给形参参数包,参数包往下不断传递,最后直接构造到链表节点上// // 直接构造// lt.emplace_back(2025, 11, 18);//// return 0;//}//class Person//{//public:// Person(const char* name ="艾莉丝wmwmwmwwmmwmwmwmwwmmwwmmw",int age = 18)// :_name(name)// ,_age(age)// { }//// // C++11// Person(const Person& p) = delete;// Person(Person&& p) = default;//// ~Person()// { }////private:// //// C++98// //Person(const Person& p);//// Alice::string _name;// int _age;//};////int main()//{// Person s1;// //Person s2 = s1;// Person s3 = std::move(s1);//// //Person s4("xxxxxxxxxxxxxxxxxxxxxxxxxxx", 1);// //s4 = std::move(s2);//// // 输出// // string(char* str)-构造// // string(char* str) - 移动构造//// return 0;//}//#include<iostream>//using namespace std;////class Example//{//public:// Example(int a,int b)// :_x(a)// ,_y(b)// {// cout << "目标构造函数\n";// }//// // 委托构造:类似于派生类复用基类// Example(int a)// :Example(a,0)// {// cout << "委托构造函数\n";// }//// int _x;// int _y;//};////class Time //{//public:// Time(int h, int m)// :_hour(h)// ,_minute(m)// { }//// // “Time”: 对委托构造函数的调用应仅为成员初始值设定项// // “_second”: 已初始化// Time(int h,int m,int s)// :Time(h,m)// //,_second(s)// { }////private:// int _hour;// int _minute;// int _second = 0;//};////int main()//{// Example(1, 2);// Example(1);//// // 输出:// // 目标构造函数// // 目标构造函数// // 委托构造函数//// return 0;//}//class Base//{//public:// Base(int x,double d)// :_x(x)// ,_d(d)// { }//// Base(int x)// :_x(x)// { }//// Base(double d)// :_d(d)// { }////protected:// int _x = 0;// double _d = 0.0;//};//////// 传统的派生类实现构造////class Dervied :public Base////{////public://// Dervied(int x):Base(x){}//// Dervied(double d):Base(d){}//// Dervied(int x,double d):Base(x,d){}////};////// C++11继承基类的所有构造函数//class Dervied :public Base//{//public:// using Base::Base;//////protected://// int _i = 0;//// string _s;//};////// 非常长//std::map<std::string, std::pair<std::string, std::string>>::iterator func();//auto func() -> std::map<std::string, std::pair<std::string, std::string>>::iterator;////int main()//{// Dervied d1(1);// Dervied d2(1.1);// Dervied d3(2,2.2);//// return 0;//}// ==========================lambda==========================// -----------------------lambda表达式语法--------------------------//int main()//{// //// 一个简单的lambda表达式// //auto add1 = [](int x, int y)->int {return x + y; };//// auto add1 = [](int x, int y) {return x + y; }; // 返回值类型可写可不写,编译器会自动推导// cout << add1(1, 2) << endl;//// // 1、捕捉为空也不能省略 // // 2、参数为空可以省略 // // 3、返回值可以省略,可以通过返回对象自动推导 // // 4、函数题不能省略// auto func1 = []// {// cout << "hello Alice" << endl;// return 0;// };// func1();//// int a = 0, b = 1;// auto swap1 = [](int& x, int& y)// {// int tmp = x;// x = y;// y = tmp;// };// swap1(a, b);// cout << a << ":" << b << endl;//// // 输出:// // 3// // hello Alice// // 1:0//// return 0;//}// ---------------------------lambda的应用-------------------------------//struct Goods//{// string _name; // 名字// double _price; // 价格// int _evaluate; // 评价//// // ...// Goods(const char* str,double price,int evaluate)// :_name(str)// ,_price(price)// ,_evaluate(evaluate)// { }//};////struct ComparePriceLess//{// bool operator()(const Goods& gl, const Goods& gr)// {// return gl._price < gr._price;// }//};////struct ComparePriceGreater//{// bool operator()(const Goods& gl, const Goods& gr)// {// return gl._price > gr._price;// }//};////struct CompareEvaluateGreater//{// bool operator()(const Goods& gl, const Goods& gr)// {// return gl._evaluate < gr._evaluate;// }//};////struct CompareEvaluateLess//{// bool operator()(const Goods& gl, const Goods& gr)// {// return gl._evaluate < gr._evaluate;// }//};////int main()//{// vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3}, { "菠萝", 1.5, 4 } };//// // 类似这样的场景,我们实现仿函数对象或者函数指针支持商品中 // // 不同项的比较,相对还是比较麻烦的,那么这里lambda就很好用了 //// //sort(v.begin(), v.end(), ComparePriceLess());// //sort(v.begin(), v.end(), ComparePriceGreater());// //sort(v.begin(), v.end(), CompareEvaluateLess());// //sort(v.begin(), v.end(), CompareEvaluateGreater());//// //auto priceLess = [](const Goods& gl, const Goods& gr)// // {// // return gl._price < gr._price;// // };//// //sort(v.begin(), v.end(), priceLess);//// sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {// return gl._price < gr._price;// });//// sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {// return gl._price > gr._price;// });//// sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {// return gl._evaluate < gr._evaluate;// });//// sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {// return gl._evaluate > gr._evaluate;// });//// return 0;//}// --------------------------捕捉列表-------------------------int x =0;// 捕捉列表必须为空,因为全局变量不用捕捉就可以用,没有可被捕捉的变量auto func1 =[](){ x++;};classA{public:voidFunc(){int x =0, y =1;// 隐式捕捉auto f1 =[=]{ _a1++;return x + y + _a1 + _a2;}; cout <<f1()<< endl;auto f2 =[&]{ x++; _a1++;return x + y + _a1 + _a2;}; cout <<f2()<< endl;// 捕捉this本质是可以访问成员变量auto f3 =[x,this]{ _a1++;return x + _a1 + _a2;}; cout <<f3()<< endl;}private:int _a1 =0;int _a2 =1;};intmain(){// 只能用当前lambda局部域捕捉的对象和全局对象// 捕获列表的意义,本质更方便的使用当前局部域的对象int a =0, b =1, c =2, d =3;// auto func1=[a,&b] () mutableauto func1 =[a,&b]{// 值捕捉的变量不能修改,引用捕捉的变量可以修改// a++ b++;int ret = a + b; x++;return ret;}; cout <<func1()<< endl;// 隐式值捕捉// 用了哪些变量就捕捉哪些变量auto func2 =[=]{int ret = a + b + c;return ret;}; cout <<func2()<< endl;// 隐式值捕捉// 用了哪些变量就捕捉哪些变量auto func3 =[&]{ a++; c++; d++;};func3(); cout << a <<" "<< b <<" "<< c <<" "<< d << endl;// 混合捕捉1auto func4 =[&,a,b]{//a++;//b++; c++; d++;return a + b + c + d;};func4(); cout << a <<" "<< b <<" "<< c <<" "<< d << endl;// ----------------验证:lambda底层是编译器生成的仿函数(更轻量级的)-------------------// 仿函数//class lambda5//{//public:// lambda5(int a_,int b_)// :a(a_)// ,b(b_)// { }// int operator()(int x)// {// ++b;// return a + b + x;// }//private:// const int a;// int& b;//};//// 运行结果://// 2//// 4//// 1 2 3 4//// 1 2 4 5// lambdaauto func5 =[a,&b](int x){++b;return a + b + x;};// 等价于// lambda func5(a, b);func5(1);//// 运行结果://// 2//// 4//// 1 2 3 4//// 1 2 4 5// 结果完全一样!猜想得到验证!return0;}

结尾

uu们,本文的内容到这里就全部结束了,艾莉丝再次感谢您的阅读!

结语:希望对学习C++相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!

往期回顾:

【C++:C++11】深入浅出 C++11:右值引用、移动语义、可变参数模板与完美转发详解

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡૮₍ ˶ ˊ ᴥ ˋ˶₎ა

Read more

【C++ 】智能指针:内存管理的 “自动导航仪”

【C++ 】智能指针:内存管理的 “自动导航仪”

目录 一、引入 二、智能指针的两大特性: 1、RAII 特点: 好处: 2、行为像指针 三、智能指针起初的缺陷:拷贝问题 四、几种智能指针的介绍。 1、C++98出现的智能指针——auto_ptr auto_ptr解决上述拷贝构造的问题: 2、boost库 3、unique_ptr 4、shared_ptr 引用计数的实现: 赋值运算符的问题:(循环引用) 5、weak_ptr 特点: 解决循环引用问题: 五、C++智能指针的基本框架: 六、定制删除器,以及包装器的使用场景之一 七、内存泄漏: 1、什么是内存泄漏,内存泄漏的危害:

By Ne0inhk
【探寻C++之旅】C++11 深度解析:重塑现代 C++ 的关键特性

【探寻C++之旅】C++11 深度解析:重塑现代 C++ 的关键特性

请君浏览 * 前言 * 1. C++的发展历史 * 2. 列表初始化:统一对象初始化的优雅方案 * 2.1 从 C++98 到 C++11 的突破 * 2.2 std::initializer_list:容器初始化的 “神器” * 3. 右值引用和移动语义:彻底解决拷贝性能痛点 * 3.1 左值 vs 右值 * 3.2 左值引用 vs 右值引用 * 3.3右值引用的使用场景 * 3.3.1参数匹配 * 3.3.2 类型分类 * 3.3.3 移动构造和移动赋值

By Ne0inhk
C++ 虚函数与纯虚函数:多态的核心实现基石

C++ 虚函数与纯虚函数:多态的核心实现基石

C++ 虚函数与纯虚函数:多态的核心实现基石 💡 学习目标:深度理解虚函数与纯虚函数的本质区别,掌握虚函数表的底层原理,能够灵活运用二者设计具备多态特性的类结构。 💡 学习重点:虚函数的声明与重写规则、纯虚函数与抽象类的使用场景、虚函数表的工作机制、虚函数的常见陷阱与解决方案。 一、虚函数的本质与定义 ✅ 结论:虚函数是 C++ 实现动态多态的核心,通过在基类成员函数前添加 virtual 关键字,允许派生类重写该函数,并在运行时根据对象的实际类型调用对应版本。 1.1 虚函数的声明语法 虚函数的声明必须在基类中进行,语法格式如下: class 基类名 {public:virtual 返回值类型 函数名(参数列表){// 函数体}}; 1.2 虚函数的核心特性 1. 运行时绑定:函数调用关系在程序运行时确定,而非编译时。 2. 重写规则:派生类重写的函数必须与基类虚函数的函数名、参数列表、返回值类型完全一致(协变类型除外)。 3.

By Ne0inhk
Redis核心通用命令深度解析:结合C++ redis-plus-plus 实战指南

Redis核心通用命令深度解析:结合C++ redis-plus-plus 实战指南

前言:为何选择 Redis 与 C++? 在当今这个数据驱动的时代,高性能的数据存储与访问是构建现代化应用的基石。Redis,作为一个开源的、基于内存的键值对存储数据库,以其无与伦比的读写速度、丰富的数据结构、以及灵活的应用场景(缓存、消息队列、会话存储、排行榜等),成为了后端开发者的瑞士军刀。 与此同时,C++ 作为一门追求极致性能的编程语言,长期以来在游戏开发、金融交易、高性能计算等领域占据着主导地位。当 C++ 的高性能与 Redis 的高速度相结合时,我们便能够构建出响应迅捷、吞吐量巨大的应用程序。 然而,要将二者优雅地结合起来,我们需要一个强大的“桥梁”——Redis 客户端库。redis-plus-plus 就是这样一个专为现代 C++ (C++11 及以上) 设计的优秀库。它不仅封装了 Redis 的原生协议,提供了类型安全、易于使用的 API,

By Ne0inhk