1. 统一的列表初始化
1.1{}初始化
struct Point
{int _x;int _y;
};
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 array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// C++11中列表初始化也可以适用于new表达式中int* pa = new int[4] { 0 };//C++98Date d1(2022, 1, 1);//C++11Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 3 };return 0;
}
1.2 std::initializer_list
int main()
{vector<int> v = { 1,2,3,4 };list<int> lt = { 1,2 };// 这里{"sort", "排序"}会先初始化构造一个pair对象map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };// 使用大括号对容器赋值v = { 10, 20, 30 };return 0;
}
以vector为例,讲一下initializer_list的使用:
在C++11中,vector新增了如下的构造函数
//先构造再拷贝 -> 编译器优化 -> 直接构造vector<int> v1 = { 1,2,3,4,5,6,7,8,9 };//直接构造vector<int> v2{ 1,2,3,4,5,6,7,8,9 };
2. 声明
2.1 auto
2.1.1 auto的使用细则
1. auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
2.1.2 auto不能推导的场景
2.2 decltype
关键字decltype将变量的类型声明为表达式指定的类型
template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret;cout << typeid(ret).name() << endl;
}
int main()
{const int x = 1;double y = 2.2;decltype(x * y) ret; // ret的类型是doubledecltype(&x) p; // p的类型是int*cout << typeid(ret).name() << endl;cout << typeid(p).name() << endl;F(1, 'a');return 0;
}
2.3 nullptr
3. 基于范围的for循环
3.1 范围for的语法
int main() {int array[] = { 1, 2, 3, 4, 5 };// 如果要进行修改,就必须要引用for (auto& e : array)e *= 2;// 如果只是访问,可以不用引用for (auto e : array)cout << e << " " << endl;return 0;
}
3.2 范围for的使用条件
int main() {vector<int> v = { 1, 2, 3, 4, 5 };vector<int>::iterator it = v.begin();for (auto e : v)cout << e << " ";cout << endl;for (; it != v.end(); it++)cout << *it << " ";return 0;
}
4. STL中一些变化
4.1 新容器
在原有容器的基础上新加了如下所标记的容器
4.2 新方法
1.提供了cbegin和cend方法返回const迭代器等等
但是实际意义不大,因为begin和end也是可以返回const迭代器的。
2.提供了各容器的initializer_list构造
3.push系列、insert、emplace等函数增加了右值引用的插入版本
4.容器增加了移动构造和移动赋值
总结:C++11引入了右值引用和移动语义,解决了左值引用的一些问题并极大提高了效率
5. 新的类功能
5.1 默认成员函数
5.2 类成员变量初始化
5.3 强制生成默认函数的关键字default
5.4 禁止生成默认函数的关键字delete
5.5 继承和多态中的final与override关键字
1.final: 修饰类时,它表示该类不能被继承
修饰虚函数时,它表示该虚函数不能在子类中被重写
2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
6. 可变参数模板
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
1. 递归函数方式展开参数包
// 每次递归都会获取并显示1个参数直至全部参数获取完就结束
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;
}
2. 逗号表达式展开参数包
PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式:
函数中的逗号表达式:(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函数打印参数,这个数组的目的纯粹是为了在数组构造的过程展开参数包。
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;
}
3. STL容器中的empalce相关接口函数
std::vector::emplace_back
template <class... Args> void emplace_back (Args&&... args);std::list::emplace_back
template <class... Args> void emplace_back (Args&&... args);
STL中的容器大多都实现了可参数列表式的empalce式的接口函数,它和insert、push式的函数比较如下:
1. 插入类型是单个值,两个没什么区别
2. 直接给插入对象参数时,empalce系列对于深拷贝的类对象,减少一次移动构造,对于浅拷贝的类对象,减少一次拷贝构造
7. lambda表达式
7.1 书写格式
void Test()
{auto f = [] {cout << "hello world" << endl; };f();
}class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{auto DateLess = [](const Date* p1, const Date* p2){return p1 < p2;//偷懒直接比指针};cout << typeid(DateLess).name() << endl;// lambda对象支持拷贝构造auto copy(DateLess);// lambda对象禁掉默认构造// decltype(DateLess) xx;//运行报错//为了下面的代码运行通过,还得有比较对象DateLess,因为lambda对象不会默认构造priority_queue<Date*, vector<Date*>, decltype(DateLess)> p1(DateLess);return 0;
}
7.2 函数对象与lambda表达式
class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}
private:double _rate;
};int main()
{double rate = 0.49;Rate r1(rate);r1(10000, 2);auto r2 = [=](double monty, int year)->double {return monty * rate * year;};r2(10000, 2);auto f1 = [] {cout << "hello world" << endl; };auto f2 = [] {cout << "hello world" << endl; };f1();f2();return 0;
}
8. 包装器
8.1 function包装器
//std::function在头文件<functional>
// 类模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;模板参数说明:
Ret : 被调用函数的返回类型
Args…:被调用函数的形参
function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器,是对可调用对象的再封装,统一类型,可调用对象有函数指针/函数名、仿函数、lambda表达式等。
对于编译器而言,下面的代码模useF板会实例化3份:
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()
{// 函数名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进行封装后得到的可调用类型是一样的,实例化就只有1份,function的使用无疑减小了模板实例化的开销:
int main()
{// 函数指针function<double(double)> fc1 = f;fc1(11.11);cout << useF(fc1, 11.11) << endl;// 函数对象function<double(double)> fc2 = Functor();fc2(11.11);cout << useF(fc2, 11.11) << endl;// lambda表达式function<double(double)> fc3 = [](double d)->double { return d / 4; };fc3(11.11);cout << useF(fc3, 11.11) << endl;return 0;
}
除了上述的可调用对象,function还能对类的(静态)成员函数进行封装:
int f(int a, int b)
{return a + b;
}class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{// 普通函数function<int(int, int)> fc1 = f;cout << fc1(1, 1) << endl;// 静态成员函数function<int(int, int)> fc2 = &Plus::plusi;cout << fc2(1, 1) << endl;// 非静态成员函数// 非静态成员函数需要对象的指针或者对象去进行调用// 因为非静态成员函数还有一个隐式参数:this/*Plus plus;function<double(Plus*, double, double)> fc3 = &Plus::plusd;cout << fc3(&plus, 1, 1) << endl;*/function<double(Plus, double, double)> fc3 = &Plus::plusd;cout << fc3(Plus(), 1, 1) << endl;return 0;
}
8.2 bind
template <class Fn, class... Args>/* unspecified */ bind (Fn&& fn, Args&&... args);template <class Ret, class Fn, class... Args>/* unspecified */ bind (Fn&& fn, Args&&... args);
1. 调整参数顺序(意义不大)
int Sub(int a, int b)
{return a - b;
}class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a - b;}
};int main()
{// 调整参数顺序,意义不大int x = 10, y = 20;cout << Sub(x, y) << endl;auto f1 = bind(Sub, placeholders::_2, placeholders::_1);cout << f1(x, y) << endl;function<double(Plus, double, double)> fc3 = &Plus::plusd;cout << fc3(Plus(), 1, 1) << endl;return 0;
}
2. 调整参数个数(意义大)
int main()
{// 调整参数的个数// 某些参数绑死function<double(double, double)> fc4 = bind(&Plus::plusd, Plus(), placeholders::_1, placeholders::_2);cout << fc4(2, 3) << endl;function<double(double)> fc5 = bind(&Plus::plusd, Plus(), placeholders::_1, 20);cout << fc5(2) << endl;return 0;
}