个人主页:Lei宝啊
愿所有美好如期而遇
统一的初始化列表
{}初始化
这种初始化方式我们建议用第一种,但是以后看见下面两种也不要感到疑惑,是可以这样初始化的。
内置类型初始化
int main()
{int a = 1;int b = { 1 };int c{ 1 };return 0;
}
自定义类型初始化
我们简单实现一个日期类
class Date
{
public:Date(){}Date(int year, int month, int day):_year(year),_month(month),_day(day){}private:size_t _year;size_t _month;size_t _day;};int main()
{Date a;Date b(1, 1, 1);Date c{ 1,1,1 };//多参数的隐式类型转换,{1,1,1}构造一个Date类型的临时对象,去拷贝构造d对象。Date d = { 1,1,1 };return 0;
}
容器对象的初始化(原理是std::initializer_list)
以vector为例:
#include <vector>
using namespace std;int main()
{//与Date是不同的,这里不是多参数隐式类型转换,//是C++11特性,添加了initializer_list构造函数vector<int> v = { 1,2,3,4,5,6 };/*底层是这样的vector(initializer_list<value_type> il){for(auto &e : il){*finish = e;finish++;}} */vector<int> v{ 1,2,3,4,5,6 };return 0;
}
initializer_list是个模板类,只要我们使用{},像下面那样,那么类型就是initializer_list。
auto il = { 10, 20, 30 }; // the type of il is an initializer_list
所有容器在C++11都新增了这样的构造函数。
所以我们可以这样使用:
vector<int> v = { 1,2,3,4,5,6 };
原理就是{1,2,3,4,5,6}先构造一个initializer_list对象,再使用这个对象进行拷贝构造,或者我们可以直接这样:
//直接构造
vector<int> v({ 1,2,3,4,5,6 });//其实就类似于这样://单参数隐式类型转换,先构造再拷贝构造
//string s = "hello world !";//直接构造
//string s("hello world !");
那么我们走一遍这个代码的流程:vector<int> v = { 1,2,3,4,5,6 };
首先v需要进行拷贝构造,而构造他的对象的类型应该是initializer_list<int>,所以使用{1,2,3,4,5,6}构造一个Initializer_list<int>的对象,然后对v进行拷贝构造。
在这个拷贝构造中,将initializer_list对象中的元素全部取出挨个赋给vector。
以map为例:
map<string, int> m3 = {{"1",1},{"2",2}};
map元素的类型为pair<string,int>,并且需要进行拷贝构造,这样的一个对象类型为initializer_list<pair<string,int>>,于是需要使用{"1",1},{"2",2}构造一个pair<string,int>类型的对象,我们看pair:
所以构造pair时first_type的类型应该是const char*,而不是string,所以这里还需要多一层构造。
C++11于是新增了这样的构造函数:
我们写代码来理解这个构造函数:
template<T1, T2>struct pair{//C++98pair(const T& key, const T& value):fisrt(key),second(value){}//C++11template<U, V>pair(U&& a, V&& b):first(a),second(b){}T1 first;T2 second;}
之前,C++98是这样的,pair(const string& key, const int& value)
现在,C++11是这样的,pair(const char*&& a, int&& b),于是这里进行了直接构造,省却了一层拷贝构造,节省了资源。
声明
3.1 auto
3.2 decltype
3.3 nullptr
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define nullptr ((void *)0)
#endif
#endif
这样nullptr就表示空指针。
右值引用和移动语义
左值引用、右值引用及移动语义-CSDN博客,这个是博主单独写的(所以也可以看出右值引用的移动语义在C++11中的地位,很重要),这里不再重复去写了。
模板中的万能引用
#include <iostream>
#include <string>
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;
}
但是为什么全成了左值引用呢?我们看PerfectForward函数,我们传右值过去时,也就是右值引用,而右值引用 引用右值后,右值引用属性变为左值,所以调用的全是左值的函数。
于是我们提到完美转发。
完美转发
template<typename T>
void PerfectForward(T&& t)
{//forward<T>(val) 完美转发Fun(forward<T>(t));
}
新的类功能
- 构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值重载
- 取地址重载
- const 取地址重载
- 如果你没有实现移动构造,析构函数,拷贝构造,以及拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动构造,否则,不生成。
- 如果你没有实现移动赋值重载,析构函数,拷贝构造,以及拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值重载,否则,不生成。
- 如果你提供了移动赋值或移动构造,那么编译器不会提供默认拷贝构造和默认拷贝赋值重载。
- 如果你提供了某些让编译器不愿意为你主动生成默认函数的函数,那么你可以使用default让他生成默认成员函数。
- 如果你什么函数也不提供,但是也不愿意让编译器提供默认函数,那么你可以使用delete不让他以及所有人生成。
举例一:
于是我们使用default:
举例二:
可变模板参数
可变参数模板是模板编程时,模板参数(template parameter)的个数可变的情形。这种模板类型支持一个函数或模板类可以接受任意多个参数,且这些参数可以是任何类型。
可变参数模板是C++ 11新增的最强大的特性之一,它对参数进行高度泛化,能表示0到任意个数、任意类型的参数。
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
#include <iostream>
#include <string>
using namespace std;//终止函数
template<class T>
void _ShowList(T value)
{//...cout << value << endl;
}template<class T, class ...Args>
void _ShowList(T value, Args... args)
{//...cout << value << " ";_ShowList(args...);
}template <class ...Args>
void ShowList(Args... args)
{_ShowList(args...);
}int main()
{ShowList("string", 1, '?');return 0;
}
lambda表达式
在C++98中,如果我们使用sort进行排序,那么我们可以通过控制传给sort的比较对象来进行不同的排序方式:
即不同的comp对象,他可以是静态成员函数名,也可以是重载了operator()的类的对象。
如果我们要对这样的一组数据按照不同方式进行比较:
struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
那么我们要去写多个不同的仿函数吗?这是不是很麻烦?
所以我们使用lambda表达式就会比较简单,现在我们先介绍lambda表达式:
lambda 表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }捕捉列表 参数列表 修饰符 返回值 函数体
参数解释说明:
[capture-list] 捕捉列表:该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda 函数使用。
- [var]:表示值传递方式捕捉变量var
- [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
- [&var]:表示引用传递捕捉变量var
- [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
- [this]:表示值传递方式捕捉当前的this指针
(parameters) 参数列表:类似于普通函数的参数列表,相同用法。(若不需要参数传递,可以省略)
mutable:当我们使用捕捉列表以值传递方式进行捕捉,使用mutable取消他的常量性,使其在函数体中可以进行修改。(其实用处不大)
return-type:返回值类型,没有返回值时默认为void,此时可省略返回值类型,当返回值类型明确时,返回值类型也可以省略。
statement:函数体,不可省略,在函数体内,不仅可以使用参数,还可以使用捕获到的变量。
举例使用:
举例一:
#include <iostream>
using namespace std;int main()
{int a = 0, b = 1;// [](int a, int b){return a + b; };auto add = [](int a, int b) ->int {return a + b; };cout << add(1, 2) << endl;return 0;
}
举例二:
int main()
{int a = 0, b = 1;auto add = [a, b]() {return a + b; };cout << add() << endl;return 0;
}
举例三:
int main()
{int a = 1, b = 2;auto add = [](int& a, int& b) {int temp = a;a = b;b = temp;};add(a, b);cout << "a: " << a << endl;cout << "b: " << b << endl;return 0;
}
举例四:
int main()
{int a = 0, int b = 1;auto add = [a, b]() {int temp = a;a = b;b = temp;};return 0;
}
int main()
{int a = 0, b = 1;auto add = [a, b]()mutable {int temp = a;a = b;b = temp;};add();cout << "a: " << a << endl;cout << "b: " << b << endl;return 0;
}
int main()
{int a = 0, b = 1;auto add = [&a, &b](){int temp = a;a = b;b = temp;};add();cout << "a: " << a << endl;cout << "b: " << b << endl;return 0;
}
int main()
{int a = 0, b = 1;auto add = [&](){int temp = a;a = b;b = temp;};add();cout << "a: " << a << endl;cout << "b: " << b << endl;return 0;
}
int main()
{int a = 0, b = 1;auto add = [=]()mutable{int temp = a;a = b;b = temp;};add();cout << "a: " << a << endl;cout << "b: " << b << endl;return 0;
}
举例五:
混合使用
int main()
{int a = 0, b = 1, c = 2;//所有变量以值传递方式捕捉,除了c,c以引用传递方式捕捉auto add1 = [=, &c]() {return a + b + c; };//所有变量以引用传递方式捕捉,除了c,c以值传递方式捕捉auto add2 = [&, c]() {return a + b + c; };return 0;
}
举例六:
int main()
{int a = 1, b = 2;auto print = [] {cout << "hello world !\n"; };print();return 0;
}
lambda表达式的底层其实就是仿函数。
解决排序:
#include <vector>
#include <algorithm>struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str),_price(price),_evaluate(evaluate){}
};struct compare
{bool operator()(const Goods& a, const Goods& b){return a._price > b._price;}
};int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 }, { "菠萝", 1.5, 4 } };//sort的compare对象要进行排序的是v的元素,也就是Goods的对象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(), compare());return 0;
}
包装器
function
普通函数:
double func(double a)
{return a / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;//f是什么?仿函数?函数名?lambda表达式?return f(x);
}int main()
{/*下面这些代码会实例化出三份useF函数*/// 函数名cout << useF(func, 11.11) << endl;// 函数对象cout << useF(Functor(), 11.11) << endl;// lamber表达式cout << useF([](double d){ return d / 4; }, 11.11) << endl;return 0;
}
#include <functional>double func(double a)
{return a / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;//f是什么?仿函数?函数名?lambda表达式?return f(x);
}int main()
{// 函数名std::function<double(double)> func1 = func;cout << useF(func1, 11.11) << endl;// 函数对象std::function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;// lamber表达式std::function<double(double)> func3 = [](double d)->double {return d /4;};cout << useF(func3, 11.11) << endl;return 0;
}
我们发现function函数只实例化出一份,并且我们上面的这些只是针对于普通函数而言,成员函数我们还没有说过。
成员函数:
#include <functional>int Plus(int a, int b)
{return a + b;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{function<int(Sub, int, int)> f = &Sub::sub;cout << f(Sub(), 1, 2) << endl;return 0;
}
注意:静态成员函数包装同普通函数。
bind
#include <iostream>
#include <functional>using namespace std;int Plus(int a, int b)
{return a - b;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{/*一般函数*/function<int(int, int)> f1 = Plus;//调换参数顺序auto func = bind(Plus, placeholders::_2, placeholders::_1);cout << func(3, 4) << endl;//调整参数个数auto func1 = bind(Plus, 20, placeholders::_1);cout << func1(10) << endl;/*成员函数:1:静态(同一般)2:非静态*/function<int(Sub, int, int)> f2 = &Sub::sub;f2(Sub(), 2, 3);//改变参数数量auto func2 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);function<int(int, int)> f3 = func2;cout << f3(3, 4) << endl;cout << func2(3, 4) << endl;return 0;
}