统一的列表初始化

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
using namespace std;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;}private:int _year;int _month;int _day;
};int main()
{int x1 = 1;// 能看懂,但不建议使用int x2 = { 2 };int x3 { 2 };Date d1(2022, 11, 22);// ->调用构造函数// 能看懂,但不建议使用Date d2 = {2022, 11, 11}; // ->调用构造函数Date d3{ 2022, 11, 11 };// ->调用构造函数vector<int> v1 = { 1, 2, 3, 4, 5, 6 };vector<int> v2 { 1, 2, 3, 4, 5, 6 };list<int> lt1 = { 1, 2, 3, 4, 5, 6 };list<int> lt2{ 1, 2, 3, 4, 5, 6 };auto x = { 1, 2, 3, 4, 5, 6 };cout << typeid(x).name() << endl;return 0;
}

  • C++11中增大了{ }的使用范围,要求能看懂,但是不建议使用

真正用途

#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
using namespace std;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;}private:int _year;int _month;int _day;
};int main()
{Date d1(2022, 11, 22);Date d2 = {2022, 11, 11}; Date d3{ 2022, 11, 11 };vector<Date> v3 = {d1, d2, d3};// C++11中{}的真正用途vector<Date> v4 = { { 2022, 1, 1 }, {2022, 11, 11} };map<string, string> dict = { { "sort", "排序" }, { "insert", "插入" } };// 赋值重载// 这里的auto无法自动推导,所以自己显示类型initializer_list<pair<const string, string>> kvil = { { "left", "左边" }, { "left", "左边" } };dict = kvil;return 0;
}

  • vector<Date> v4 = { { 2022, 1, 1 }, {2022, 11, 11} };

initializer_list

  • C++11增加了 initializer_list,以及对vector和list等容器的更新,所以才支持上面的用法

总结:

  • C++11以后一切对象都可以用列表初始化,但是建议普通对象还是用以前的方式初始化,
  • 容器如果有需求的话就可以用列表初始化

decltype(拿到变量的类型)

#include <iostream>
using namespace std;int main()
{int x = 10;// typeid拿到只是类型的字符串,不能用这个再去定义对象什么的//typeid(x).name() y = 20;decltype(x) y1 = 20.22;auto y2 = 20.22;cout << y1 << endl;cout << y2 << endl;return 0;
}

  • typeid拿到只是类型的字符串,不能用这个再去定义对象什么的
  • decltype不仅仅可以拿到变量的类型,还可以去定义对象

C++11新增的容器

  • unordered_mapunordered_set比较有用,arrayforward_list的价值不大

容器array

#include <iostream>
#include <array>
using namespace std;int main()
{const size_t N = 100;int a1[N];// C语言数组越界检查,越界读基本检查不出来,越界写是抽查a1[N];//a1[N] = 1;//a1[N + 5] = 1;// 越界读写都可以被检查出来array<int, 1> a2;a2[N];a2[N] = 1;a2[N + 5] = 1;return 0;
}
  • array容器只要是越界就一定能被检查出来
  • C语言数组越界检查,越界读基本检查不出来,越界写是抽查

array实际使用情况:

  • array用得很少,一方面大家用c数组用惯了
  • 用array不如用vector + resize去替代c数组

左值引用& 和 右值引用&&

#include <iostream>
#include <utility>
using namespace std;int main()
{// 左值: 能取地址int* p = new int(0);int b = 1;const int c = 2;// 对左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;// 右值:不能取地址10;1.1 + 2.2;// 对右值的右值引用int&& rr1 = 10;double&& rr2 = 1.1 + 2.2;// 对右值的左值引用// double& r1 = 1.1 + 2.2;// errorconst double& r1 = 1.1 + 2.2;// 对左值的右值引用//int&& rr5 = b;// errorint&& rr5 = move(b);return 0;
}
  • 左值: 能取地址
  • 右值: 不能取地址,如:临时对象,const的不能被修改的对象
  • 如果对右值使用左值引用,需要加上const
  • 如果对左值使用右值引用,需要是使用move函数

左值引用可以解决的问题

  1.  做参数,a. 减少拷贝 b.做输出型参数
  2. 做返回值,a.减少拷贝, 提高效率 b.引用返回,可以修改返回对象(比如: operator[])

左值引用无法解决的问题

  • 引用返回的前提是返回值出了作用域之后还在,
  • 但是无法解决string中的to_string的返回值,以及有些函数返回值是二维数组的问题

右值引用的实际用途: 移动构造 + 移动赋值

没有移动构造

  • 这里的string需要自己写,才能看到结果
  • 这里的g++编译器优化的更厉害,这里虽然定义了ret
    但是没有使用,所以g++一次都没有拷贝构造
  • -fno-elide-constructors可以取消编译器的优化

加上移动构造

  •  函数参数的匹配原则是会优先匹配最合适自己的参数,
  • to__string(-3456)中的-3456是一个右值,会优先匹配到移动构造,会发生右值引用返回
  • 右值:1.内置类型右值-纯右值 2. 自定义类型右值 - 将亡值
  • 将亡值就是快要亡了的值,所以它的值就可以直接交换(swap)
  • 将拷贝构造变成移动构造,会极大的提高效率

没加移动赋值

  • g++编译器优化的很厉害

加上移动赋值

  • 移动赋值和移动构造一样,减少了拷贝,提高了效率

  • C++11以后,几乎所有的容器插入接口都提供了右值版本
  • 插入过程中,如果传递对象是右值对象,那么就会进行资源转移减少拷贝

引用折叠

#include <iostream>
#include <utility>
using namespace std;void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }// 万能引用:t既能引用左值,也能引用右值
// 引用折叠
template<typename T>
void PerfectForward(T&& t)
{Fun(t);
}int main()
{PerfectForward(10);           // 右值int a;PerfectForward(a);            // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);           // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

  • 模板参数的右值引用叫万能引用,T&& t中的t即能引用左值,也能引用右值
  • 这里会触发引用折叠,编译器会把它识别成左值

完美转发解决引用折叠问题

  • 完美转发:保持t引用对象属性(是编译器能分出左值和右值)
  • 它通过std::forward<模板>(参数)实现

新的类功能

 C++中默认成员函数

  • 构造 析构 拷贝构造
  • 赋值重载 取地址重载 const取地址重载
  • 和C++11新增的移动构造,移动赋值
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
  • 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个。
  • 那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,
    • 对于内置类 型成员会执行逐成员按字节拷贝
    • 自定义类型成员,则需要看这个成员是否实现移动构造,
      • 如果实现了就调用移动构造,没有实现就调用拷贝构造
  • 移动赋值运算符重载同上

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

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

可变参数模板

#include <iostream>
using namespace std;// 可变参数的函数模板
template <class ...Args>
void ShowList(Args... args)
{cout << sizeof...(args) << endl;
}int main()
{string str("hello");ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', str);return 0;
}

  • sizeof...(args)// 求模板参数的个数

使用可变参数模板

// 解决只有一个参数的情况
void ShowList()
{cout << endl;
}// Args... args代表N个参数包(N >= 0)
template <class T, class ...Args>
void ShowList(const T& val, Args... args)
{cout << "ShowList("<<val<<", " << sizeof...(args) << "参数包)" << endl;ShowList(args...);
}int main()
{string str("hello");ShowList(1, 'A', str);return 0;
}

  • 通过递归调用去使用模板参数

容器的emplace_back和push_back比较

#include <list>
#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year = 1, int month = 1, int day = 1)" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}Date& operator=(const Date& d){cout << "Date& operator=(const Date& d))" << endl;return *this;}private:int _year;int _month;int _day;
};int main()
{list<Date> lt1;cout << "---------------------------------" << endl;lt1.push_back(Date(2022, 11, 16));cout << "---------------------------------" << endl;lt1.emplace_back(2022, 11, 16);cout << "---------------------------------" << endl;return 0;
}

  • emplace_back和push_back对内置类型的处理是一样的
  • 因为emplace_back可以直接传参数,就只会发生构造,比push_back少一次的拷贝

lambda表达式

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
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;}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, \{ "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}
  • 在c++11之前要通过不同的标准排序

  • 就需要传不同的仿函数才能解决问题


#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
struct Goods
{string _name;double _price; //价格int _evaluate; //评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price;});sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price;});sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate < g2._evaluate;});sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate;});
}
  • 引入lambda解决问题

lambda表达式语法

[捕捉列表](参数列表)mutable->返回值类型{函数体实现}

  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
    性。使用该修饰符时, 参数列表不可省略 (即使参数为空)。
注意:
  • 在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为 空
  • 因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
  • 定义了lambda,还需要调用
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{// 两个数相加的lambdaauto add1 = [](int a, int b)->int{return a + b; };cout << add1(1, 2) << endl;// 省略返回值auto add2 = [](int a, int b){return a + b; };cout << add2(1, 2) << endl;// 交换变量的lambdaint x = 0, y = 1;auto swap1 = [](int& x1, int& x2)->void{int tmp = x1; x1 = x2; x2 = tmp; };swap1(x, y);cout << x << ":" << y << endl;auto swap2 = [](int& x1, int& x2){int tmp = x1;x1 = x2;x2 = tmp;};swap2(x, y);cout << x << ":" << y << endl;return 0;
}


  • 不传参数,默认捕捉的对象不能修改

捕获列表说明

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用

  • [var]:表示值传递方式捕捉变量var
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
  • [&var]:表示引用传递捕捉变量var
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
  • [this]:表示值传递方式捕捉当前的this指针
#include <iostream>
using namespace std;static int f = 1;// 全局
int ff = 2;// 全局
int func()
{int a, b, c, d, e;a = b = c = d = e = 1;// [=] 全部传值捕捉auto f1 = [=]() {cout << a << b << c << d << e << endl;};f1();// 调用//auto f2 = [=, a]() {};// error// 混合捕捉auto f2 = [=, &a]() {a++;cout << a << b << c << d << e << endl;};f2();// 调用static int x = 0;if (a){// []也能捕捉全局变量auto f3 = [&, a]() {//a++;b++, c++, d++, e++, x++;cout << a << b << c << d << e << endl;f++, ff++;cout << f << " " << ff << endl;};f3();// 调用}return 0;
}int main()
{func();不同的栈帧,不可捕捉//auto f4 = [&, a]() {// //a++;//  b++, c++, d++, e++, x++;//    cout << a << b << c << d << e << endl;//    f++, ff++;//    cout << f << " " << ff << endl;//};return 0;
}

  • [&, a]表示除了a是值传递,其他的都是引用传递
  • auto f2 = [=, a]() {};
    • 捕捉列表不允许变量重复传递 ,否则就会导致编译错误
  • 块作用域以外的lambda函数捕捉列表必须为空。
  • 不同的栈帧,不可捕捉,比如f4要去捕捉其他栈帧的变量
#include <iostream>
using namespace std;void (*PF)();
int main()
{auto f1 = [] {cout << "hello world" << endl; };auto f2 = [] {cout << "hello world" << endl; };//f1 = f2// error// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值个相同类型的函数指针PF = f2;PF();return 0;
}
  •  lambda表达式之间不能相互赋值
  • 但是允许使用一个lambda表达式拷贝构造一个的副本
  • 但是可以将lambda表达式赋值给相同类型的函数指针

函数对象与lambda表达式

#include <iostream>
using namespace std;
class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}private:double _rate;
};// lambda_uuid
class lambda_xxxx
{};int main()
{// 函数对象double rate = 0.49;Rate r1(rate);r1(10000, 2);// 仿函数lambda_uuid// lambda -> lambda_uuidauto r2 = [=](double monty, int year)->double{return monty*rate*year; };r2(10000, 2);auto r3 = [=](double monty, int year)->double{return monty*rate*year; };r3(10000, 2);return 0;
}

  • 实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

function包装器 : 一般用来包装lambda表达式

  • Ret: 被调用函数的 返回类型
  • Args…:被调用 函数的形参
#include <iostream>
#include <functional>
using namespace std;template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}double f(double i)
{return i / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};int main()
{// 使用包装器// 函数指针function<double(double)> f1 = f;cout << useF(f1, 11.11) << endl;// 函数对象function<double(double)> f2 = Functor();cout << useF(f2, 11.11) << endl;// lamber表达式对象function<double(double)> f3 = [](double d)->double{ return d / 4; };cout << useF(f3, 11.11) << endl;// 不使用包装器// 函数名cout << useF(f, 11.11) << endl;// 函数对象cout << useF(Functor(), 11.11) << endl;// lamber表达式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;return 0;
}

  • function就是一种适配器,是 lambda的另一种引用
  • C++中的function本质是一个类模板,也是一个包装器。

案例:逆波兰表达式求值

  • 这里用包装器就很好的解决这个问题

bind 绑定

#include <iostream>
#include <functional>
#include <map>
using namespace std;
int Div(int a, int b)
{return a / b;
}int Plus(int a, int b)
{return a + b;
}int Mul(int a, int b, double rate)
{return a * b * rate;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};using namespace placeholders;int main()
{// 调整个数, 绑定死固定参数function<int(int, int)> funcPlus = Plus;//function<int(Sub, int, int)> funcSub = &Sub::sub;function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), _1, _2);function<int(int, int)> funcMul = bind(Mul, _1, _2, 1.5);map<string, function<int(int, int)>> opFuncMap = {{ "+", Plus},{ "-", bind(&Sub::sub, Sub(), _1, _2)}};cout << funcPlus(1, 2) << endl;cout << funcSub(1, 2) << endl;cout << funcMul(2, 2) << endl;cout << opFuncMap["+"](1, 2) << endl;cout << opFuncMap["-"](1, 2) << endl;cout << "------------------------" << endl;int x = 2, y = 10;cout << Div(x, y) << endl;// 调整顺序, _1表示第一个参数,_2表示第二个参数function<int(int, int)> bindFunc2 = bind(Div, _2, _1);cout << bindFunc2(x, y) << endl;return 0;
}

可以将bind函数看作是一个通用的 函数适配器 ,它接受一个可调用对象,生成一个新的可调用对

来“适应”原对象的参数列表。

调用bind的一般形式:auto newCallable = bind(callable,arg_list);

  • 其中,newCallable本身是一个可调用对象
  • arg_list是一个逗号分隔的参数列表
  • 对应给定的 callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数
  • arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示 newCallable的参数,它们占据了传递给newCallable的参数的“位置”。
    • 数值n表示生成的可调用对象中参数的位置:

      • _1为newCallable的第一个参数,_2为第二个参数,以此类推。

lesson5: C++11相关推荐

  1. Redis 笔记(11)— 文本协议 RESP(单行、多行字符串、整数、错误、数组、空值、空串格式、telnet 登录 redis)

    RESP 是 Redis 序列化协议Redis Serialization Protocol 的简写.它是一种直观的文本协议,优势在于实现异常简单,解析性能极好. ​ Redis 协议将传输的结构数据 ...

  2. Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)

    1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...

  3. 只要5分钟用数据可视化带你看遍11月份新闻热点事件

    2017年11月份已经离我们而去,在过去的11月份我们也许经历了双十一的剁手,也可能亲眼看见了别人剁手.11月份的北京大兴区发生了"11·18"重大火灾,国内多家幼儿园也多次上了头 ...

  4. 1-1 机器学习和深度学习综述-paddle

    课程>我的课程>百度架构师手把手教深度学习>1-1 机器学习和深度学习综述> 1-1 机器学习和深度学习综述 paddle初级课程 王然(学生) Notebook 教育 初级深 ...

  5. CUDA 11功能清单

    CUDA 11功能清单 基于NVIDIA Ampere GPU架构的新型NVIDIA A100 GPU在加速计算方面实现了最大的飞跃.A100 GPU具有革命性的硬件功能,CUDA 11与A100一起 ...

  6. 保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事

    保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事 10 things to consider when securing an embedded 802.11 Wi-Fi device 随着 ...

  7. CUDA 11功能展示

    CUDA 11功能展示 CUDA 11 Features Revealed 新的NVIDIA A100 GPU基于NVIDIA安培GPU架构,实现了加速计算的最大一代飞跃.A100 GPU具有革命性的 ...

  8. 深度学习11个实用技巧

    深度学习11个实用技巧 深度学习工程师George Seif发表了一篇博文,总结了7个深度学习的技巧,本文增加了几个技巧,总结了11个深度学习的技巧,主要从提高深度学习模型的准确性和速度两个角度来分析 ...

  9. 【CV】吴恩达机器学习课程笔记第11章

    本系列文章如果没有特殊说明,正文内容均解释的是文字上方的图片 机器学习 | Coursera 吴恩达机器学习系列课程_bilibili 目录 11 机器学习系统设计 11-1 确定执行的优先级:以垃圾 ...

最新文章

  1. flask 的 request
  2. 用Spring Web Flow和Terracotta搭建Web应用
  3. 如何用vue-router为每个路由配置各自的title
  4. maven helper的使用
  5. c语言中freopen函数,fopen和freopen_C中freopen和fopen的区别(用法+详解+区别)
  6. 影视或游戏:中国视效人才的「进与退」
  7. Promolike Qualtrics Dashboard
  8. Tuscany SCA 发布Web Service
  9. kafka(一)—基本概念
  10. linux ucontext族函数的原理及使用
  11. AcWing 859. Kruskal算法求最小生成树(稀疏图)
  12. 动态电压与频率调节在降低功耗中的作用
  13. 提升PPT逼格的利器!只需1招,让PPT页面化腐朽为神奇~
  14. CocosCreator之KUOKUO带你做刚体移动与物品拾取到背包
  15. 传输设备基础知识〖 一篇就看懂〗
  16. Java通过快递鸟-查询物流
  17. 电脑插入头戴式耳机后声音依然外放
  18. 做为一名程序员应该有的的好习惯
  19. shell if/then/elif/else/fi
  20. dubbo官方文档_Dubbo(一)

热门文章

  1. ORACLE DATABASE LICENSES 计算方法和收费
  2. 热点综述 | 纵向微生物组研究的统计方法总结
  3. 面试题:群聊消息的已读未读设计
  4. vue vue-element-ui组件 eltable 表头背景颜色
  5. 【Excel VBA】Len的有趣之处——获取的长度怎么不一致?
  6. html表单 多行输入文字,如何在HTML中创建多行文本输入(文本区域)?
  7. 受欢迎的牛(有向图的强连通分量)
  8. Mac 在启动时显示的各种界面图标说明:禁止符号、问号、空白屏幕、锁定等
  9. 根据地理位置获取经纬度
  10. 极米投影、坚果投影、当贝投影,三大品牌对比来了