目录
1.列表初始化
2.声明
3.右值引用和移动语句
4. c++11新的类功能
5. 可变参数模板
6.lambda表达式
7.包装器
8. 后言
1. 列表初始化
1.1 {}的初始化
(1) c++98标准规定可以使用{}对数组以及结构体进行统一的列表初始化.
struct Point
{int _x;int _y;
};int main()
{int a1[] = { 1, 2, 3, 4, 5 };int a2[5] = { 0 };Point p = { 1, 2 };return 0;
}
(2) c++11里面增加了对{}的使用范围, 可以对所以的内置类型和自定义类型的类型使用初始化列表, 可以加=;也可以不加.
struct Point
{int _x;int _y;
};int main()
{int x1 = 1;int x2{ 2 };int a1[]{ 1, 2, 3, 4, 5 };int a2[5]{ 0 };Point p{ 1, 2 };return 0;
}
(3) 创建对象也可以使用列表初始化的方式调用构造函数初始化.
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, 1, 1);Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 2 };return 0;
}
1.2 initializer_list
(1)initializer_list的类型:
(2)initializer_list的使用场景:
一般作为构造函数的参数; 方便初始化对象, 也可以是operator=的参数, 那么就可以用{}进行赋值.
c++11里面对STL的构造函数都添加了initializer_list; 就是允许{}进行对象的初始化的一种方法.支持范围for.
//int main()
//{
// auto il = { 10, 20 ,30 };
// cout << typeid(il).name() << endl;
// return 0;
//}//int main()
//{
// vector<int> v{ 1, 2, 3, 4 };
// list<int> lt{ 1, 2 };
// map<string, string> sict = { {"sort", "排序"}, {"inset", "插入"} };
//
// v = { 1, 2, 3 };
// return 0;
//}int main()
{//多参数构造类型转换: 构造+拷贝构造-> 直接构造.Date d2 = { 2023, 11, 25 };auto il1 = { 10, 20, 30, 40, 50 };initializer_list<int> il2 = { 11, 22, 33 };initializer_list<int>::iterator it2 = il2.begin();//while (it2 != il2.end())//{// cout << *it2 << endl;// it2++;//}//cout << endl;for (auto e : il2){cout << e << " ";}cout << endl;return 0;
}
2. 声明
2.1 auto
c++98里面定义auto是一种类型局部自动存储类型, but在局部域中定义的变量就是默认是局部自动存储类型, 然而在c++11里面就是自动推断类型, 必须进行显示初始化, 以便于编译器将类型设置为初始化的类型.
2.2 decltype
思考一个小问题: 如果我要使用一个我不知道的类型, 怎么做?
使用auto吗? 那肯定是不行的, auto在初始化的时候必须给定类型才可以让编译器去推断.那么就要用到decltype. 所以decltype的作用就是将变量的类型声明为表达式指定的类型.
int main()
{int i = 1;double d = 2.2;/*cout << typeid(i).name() << endl;cout << typeid(d).name() << endl;*/auto j = i;auto ret = i * d;decltype(ret) x;vector<decltype(ret)> v;v.push_back(1);v.push_back(1.1);for (auto e : v){cout << e << " ";}cout << endl;return 0;
}
2.3 nullptr
c++里面定义NULL为字面常量0; 但是这个字面常量0既可以表示指针常量, 还可以表示整形常量.所以c++11里面添加nullptr表示为空指针.
2.4 STL的一些变化
(1) array:
这个接口其实是对应vector, 其实也没啥用处.
(2) forward_list:
用来任意位置进行删除和插入操作.
(3) unordered_map
(4) unordered_set
上面两个序列式容器之前在哈希和红黑树里面都有出现, 这里不过多介绍了.
3. 右值引用和移动语句
3.1 左值和右值
左值:
一般是变量值或者是解引用的指针; 一般是在赋值符号的左边, 但是右值一定不会出现在赋值符号的左边. 可以获取到地址并且可以对它进行赋值, 如果是const就不行咧.
左值引用:
对左值进行引用, 给左值取别名.
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;return 0;
}
右值:
通常是字面常量, 表达式返回值, 函数返回值(不能是左值引用的返回值), 右值可以出现在赋值符号的右边, 但是不能获取到地址. 还有就是右值无法在赋值符号的左边; 因为左边必须是左值, 右值有常属性所以不行!
右值引用:
对右值的引用, 对右值取别名.
注意:
右值是无法取到地址的, 但是取别名之后, 被存储到特定的位置, 并且可以拿到那个位置的地址.
int main()
{double x = 1.1, y = 2.2;10;x + y;fmin(x, y);int&& rr1 = 10;int&& rr2 = x + y;int&& rr3 = fmin(x, y);return 0;
}
3.2左值引用和右值引用的区别
左值引用:
(1) 左值引用只能用于引用左值, 不能用来引用右值;
(2) but const修饰的左值引用可以引用左值和右值.
右值引用:
(1) 右值引用只能引用右值, 不能引用左值;
(2)but move修饰过的左值可以被右值引用使用.
int main()
{//左值引用int a = 10;int& ra1 = a;const int& ra2 = 10;const int& ra2 = a;//右值引用int&& aa1 = 10;int a = 10;int&& aa2 = move(a);return 0;
}
3.3 右值引用的使用场景
(1) 先看看左值引用的应用场景, 那就是作为参数或者返回值, 但是如果出了作用域还在那么就可以使用左值引用. 因为左值引用是局部变量, 出了作用域之后不在那怎么把数据传回来捏?
(2) 右值引用和移动语义的本质: 就是将参数的右值资源直接夺取过来, 然后就不用进行深拷贝了, 这就是移动构造. 进一步提高效率.
(3) 编译器在调用构造的时候, 有移动构造就调用移动构造, 没有就调用拷贝构造.
(4) 移动赋值: 重载operator=的时候使用右值引用.
3.4 右值引用左值的场景分析
右值引用左值不能直接引用, 需要加move进行将左值转化为右值才可以, 但是左值的本质属性还是左值, 并没有改变. 如果是作为拷贝构造函数的参数, 那么就一定要注意, 如果拷贝赋值move之后, 就会将原来的数据交给新的对象, 原来的对象就被置空了.
注意:右值被右值引用之后属性变成了左值.
void push_back (value_type&& val);
int main()
{list<bit::string> lt;bit::string s1("1111");// 这里调用的是拷贝构造lt.push_back(s1);// 下面调用都是移动构造lt.push_back("2222");lt.push_back(std::move(s1));return 0;
}
3.5 完美转发
使用到模板以及右值引用. 模板里面的&&是表示万能引用, 既可以接受左值, 又可以接收右值,
可以看看下面的代码:
为啥都变成左值啦? 因为右值被右值引用之后属性变成了左值.
如果我们就是想要右值引用怎么办捏?
上完美转发, std:: forward. (保持对象原生类型的属性)
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; }template<typename T>
void perfectForward(T&& t)
{//Fun(t);//完美转发Fun(forward<T>(t));
}int main()
{//右值perfectForward(10);//左值int a;perfectForward(a);perfectForward(move(a));//左值const int b = 9;perfectForward(b);perfectForward(move(b));return 0;
}
3.5.1 完美转发使用场景
template<class T>
struct ListNode
{ListNode* _next = nullptr;ListNode* _prev = nullptr;T _data;
};
template<class T>
class List
{typedef ListNode<T> Node;
public:List(){_head = new Node;_head->_next = _head;_head->_prev = _head;void PushBack(T&& x){//Insert(_head, x);Insert(_head, std::forward<T>(x));}void PushFront(T&& x){//Insert(_head->_next, x);Insert(_head->_next, std::forward<T>(x));}void Insert(Node* pos, T&& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = std::forward<T>(x); // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}void Insert(Node* pos, const T& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = x; // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}private:Node* _head;
};
int main()
{List<string> lt;lt.PushBack("1111");lt.PushFront("2222");return 0;
}
4. c++11新的类功能
c++里面6个默认构造的类分别是:
1. 构造函数; 2. 析构函数; 3. 拷贝构造函数; 4. 拷贝赋值重载; 5. 取地址重载; 6. const 取地址重载.
现在又多了移动构造和移动赋值,:
注意:
(1) 如果你没有写移动构造函数, 并且没有写析构函数, 拷贝构造函数, 拷贝赋值重载函数的话, 那么就编译器自动生成移动构造函数.
(2)如果你没有写赋值重载函数, 并且没有写析构函数, 拷贝构造函数, 拷贝赋值重载函数那么就编译器自动生成移动赋值函数.
(3)对于内置类型进行浅拷贝, 自定义类型如果实现了移动构造就使用移动构造, 如果没有使用的话就进行深拷贝.
2.强制生成默认函数的关键字: default 进行显示的移动构造生成.
3.强制不生成默认函数的关键字: delete
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;//强制不生成默认函数Person(Person&& p) = delete;private:bit::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}
5. 可变参数模板
// Args 是一个 模板参数包 , args 是一个 函数形参参数包// 声明一个参数包 Args...args ,这个参数包中可以包含 0 到任意个模板参数。template < class ... Args >void ShowList ( Args ... args ){}注意: 使用参数包是ex到家的, 还有获取参数包得值! 学的想吐血!!!获取参数值是是用到递归调用的方法.
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;
}
5.1 比较emplace和insert
emplate的接口是支持万能模板以及可变参数包的. 那和有啥区别捏? 而且emplace接口的优势在哪里捏?
支持了可变参数包, 以及emplace_back是直接构造, push_back是先构造再移动构造.
6.lambda表达式
引入: lambda底层就是仿函数, 由于当我们定义自定义类型的比较的时候, 都需要写一个类来规定比较的方法(仿函数), 使用起来非常不方便.定义就弄出来lambda表达式.
6.1lambda表达式的语法:
(1) 格式:[capture-list] (parameters) mutable -> return-type { statement }
capture-list: 捕捉列表, 捕捉上下文的变量
parameters: 参数列表; 和普通函数是一样的.
mutable: 看代码吧, 更加形象.
->returntype: 返回值类型; 和普通函数是一样的.statement:函数体 和普通函数是一样的.(2) lambda的类型是 class <lambda_a62159c664704dbd449a2ea762c73c4d>lambda + uuid;(3) lambda表达式是一个匿名函数, 如果要使用据需要auto去拿.
int main()
{//啥都不做[] {};//捕捉=上下的变量,返回值类型推到为int.int a = 3, b = 4;[=] {return a + 3; };//没有返回值, 捕捉&的上下文变量, 然后用到函数体里面/*auto fun1 = [&](int c) {b = a + c; };fun1(10);cout << a << " " << b << endl;*//*auto fun2 = [=, &b](int c)->int{return b += a + c; };cout << fun2(10) << endl;*/int x = 10;auto add_x = [x](int a)mutable {x *= 2; return a + x; };cout << typeid(add_x).name() << endl;cout << add_x(10) << endl;
}
6.2 捕捉列表
捕捉方式是传值还是传递引用都有区别:
(1) [var]: 传值捕捉
(2) [=]: 传值捕捉, 捕获子作用域里面所有变量(包括this);
(3) [&var]:表示引用传递捕捉变量var
(4) [&]:表示引用传递捕捉所有父作用域中的变量(包括this)(5) [this]:表示值传递方式捕捉当前的this指针注意:(1)捕捉列表有多个参数, 就要用 ' ,' 隔开.(2)不允许重复传递, 编译器会报错;(3)lambda表达式之间不能相互赋值;
6.3 仿函数和lambda表达式:
其实底层来看这两个东西是一样的; 仿函数只是在类里面重载operator()的对象.
7. 包装器
7.1 为啥需要包装器, 这是个啥?
(1) function是包装器也是适配器, 本质就是一个类模板.
ret = func(x);
上面这段代码可以是函数返回值, 仿函数, lambda表达式, 那你找到它到底是上面吗?
根本不知道, 而且编译器还会实例化上面出现的所有对象, 那么编译器必定效率低下.
(2) function的头文件#include<functional>
7.2 bind
bind就是一种适配器, 支持可调用对象(函数, 仿函数, lambda表达式), 生成一个新的可调用对象来适应原来的参数列表, 其中的 _n 是一种占位符, 表示可调用对象的位置.
#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()
{//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定function<int(int, int)> func1 = std::bind(Plus, placeholders::_1, placeholders::_2);auto func2 = std::bind(Plus, 1, 2);cout << func1(1, 2) << endl;cout << func2() << endl;Sub s;绑定成员函数function<int(int, int)> func3 = std::bind(&Sub::sub, s, placeholders::_1, placeholders::_2);参数调换顺序//std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,// placeholders::_2, placeholders::_1);//cout << func3(1, 2) << endl;//cout << func4(1, 2) << endl;return 0;
}
8. 后言
其中c++11里面的新增功能, 如果不使用/ 少使用很容易就忘掉, 就比如lambda, 老长的你能在很久不用记得很清楚阿? xdm多用多写! 还有给博主三联, 你绝对学的很好!!!