文章目录
C++11新特性
1. 列表初始化
内置类型初始化:
int main()
{
// 内置类型变量
int x1 = {10};
int x2{10};
int x3 = 1+2;
int x4 = {1+2};
int x5{1+2};
// 数组
int arr1[5] {1,2,3,4,5};
int arr2[]{1,2,3,4,5};
// 动态数组,在C++98中不支持
int* arr3 = new int[5]{1,2,3,4,5};
// 标准容器
vector<int> v{1,2,3,4,5};
//等号可以不用写
map<int, int>= m{{1,1}, {2,2,},{3,3},{4,4}};
return 0;
}
2. 变量类型推导
2.1 auto
int x = 0;
auto * a = &x; // a -> int*,auto被推导为int
auto b = &x; // b -> int*,auto被推导为int*
auto & c = x; // c -> int&,auto被推导为int
auto d = c; // d -> int ,auto被推导为int
const auto e = x; // e -> const int
auto f = e; // f -> int
const auto& g = x; // e -> const int&
auto& h = g; // f -> const int&
由上面的例子可以看出:
- a 和 c 的推导结果是很显然的,auto 在编译时被替换为 int,因此 a 和 c 分别被推导为 int* 和 int&。
- b 的推导结果说明,其实 auto 不声明为指针,也可以推导出指针类型。
- d 的推导结果说明当表达式是一个引用类型时,auto 会把引用类型抛弃,直接推导成原始类型 int。
- e 的推导结果说明,const auto 会在编译时被替换为 const int。
- f 的推导结果说明,当表达式带有 const(实际上 volatile 也会得到同样的结果)属性时,auto 会把 const 属性抛弃掉,推导成 non-const 类型 int。
- g、h 的推导说明,当 auto 和引用(换成指针在这里也将得到同样的结果)结合时,auto 的推导将保留表达式的 const 属性
2.2 decltype
decltype可以用一个已知类型的变量去初始化另一个变量,使新初始化的变量是我们想要声明的类型,即将变量的类型声明为表达式指定的类型。
#include<vector>
#include<map>
//场景:使用decltype定义一个auto推导对象的拷贝
int main()
{
map<string,string> dict = {{"Left","左"},{"Right","右"}};
auto it = dict.begin();
//auto copyIt = it;
//vector<auto> v;错误语法
decltype(it) copyIt = it;
vector<decltype(it)> v;
v.push_back(it);
return 0;
}
3. nullptr
C++中NULL被定义成字面量0,这样就可能出现一些问题,因为0既能表示指针常量,又能表示整形常量,所以出于清晰和安全角度的考虑,C++11新增了nullptr,用于表示空指针。
4. 范围for
5. STL中的一些变化
1.新增一些容器:
array:
#include<array>
int main()
{
//array作为容器比较鸡肋,它的作用和底层实现和数组并无二致
//但是:
//1.array支持迭代器,能够更好的兼容STL
//2.array对于越界的检查
int a1[10];
array<int,10> a2;
//a1[11] = 0; 不报错: *(a1+11) = 0
//a2[11] = 0; 报错: a2.operator[](14) = 0
return 0;
}
forward_list: 单链表
//支持头插头删,不支持尾插尾删
//相较于list,每个结点省了一个指针
//底层为哈希表
//map、set底层为红黑树
2.针对已有容器,增加了一些提高效率的接口。比如:列表初始化initializer_list、移动构造、移动赋值、右值引用(&&)版本插入接口。
6. 右值引用和移动语义
6.1 左值引用(&)和右值引用(&&)
左值引用:
无论是左值引用还是右值引用,都是给对象起别名。左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址以及可以对它赋值,左值出现在赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能对其赋值,但是可以取其地址。实际上,可以取地址的对象,就是左值。
int main()
{
//常见左值引用
int a = 10;
int &b = a;
int *p = &a;
int &c = *p;
//定义时const修饰符后的左值,不能对其赋值,但是可以取其地址。
const int d = 10;//d也是左值
const int &e = d;
return 0;
}
右值引用:
右值也是一个表示数据的表达式,如:字面常量、表达式返回值、传值返回函数的返回值(这个不能是左值引用返回)等,右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,右值不能取地址。右值引用就是给右值起别名。不能取地址的对象,就是右值。
int main()
{
double x = 1.5;
double y = 2.5;
//常见右值
10;
x+y;
fmin(x,y);
//常见右值引用,不能取地址
int &&r1 = 10;
double &&r2 = x+y;
double &&r3 = fmin(x+y);
}
交叉引用:
左值引用能否引用右值?右值引用能否引用左值?
int main()
{
double x = 1.5;
double y = 2.5;
//常见右值:
10;
x+y;
fmin(x,y);
//常见左值:
int *p = new int(0);
int a = 1;
const int b = 2;
//左值引用能否引用右值?--不能直接引用,但是const左值引用可以引用右值
//int &r1 = 10; 错误
//int &r2 = x+y; 错误
//int &r3 = fmin(x,y); 错误
const int &r1 = 10;
const double &r2 = x+y;
const double &r3 = fmin(x,y);
//右值引用能否引用左值?--不能直接引用,但是可以右值引用move以后的左值
//int *&& rr1 = p; 错误
//int && rr2 = *p; 错误
//int && rr3 = a; 错误
//const int && rr4 = b; 错误
int *&& rr1 = move(p);
int && rr2 = move(*p);
int && rr3 = move(a);
const int && rr4 = move(b);
return 0;
}
右值是不能取地址的,但是给右值取别名后,对右值引用可以取地址。因为给右值取别名后,会导致右值被存储到特定位置,且可以取到该地址。
int main()
{
double x = 1.1,y = 2.2;
int &&rr1 = 10;
double &&rr2 = x + y;
const double &&rr3 = x + y;
cout << &rr1 << endl;//可以对右值的引用取地址
cout << &rr2 << endl;//可以对右值的引用取地址
rr1 = 11;
rr2 = 22.22;
return 0;
}
6.2 右值引用的场景和意义
右值引用的产生是为了弥足左值引用的不足,在某些情况下,左值引用只能传值,调用拷贝构造进行深拷贝,而右值引用则可以调用移动构造,进行资源转移,减少资源浪费。
string& operator+=(char ch)
{
push_back(ch);
return *this;//*this是左值,出了作用域后还在,允许引用返回
}
string operator+(char ch)
{
string tmp(*this);
push_back(ch);
//只能传值返回,因为出了作用域若使用引用返回则返回tmp的别名
//但是此时已经析构,tmp返回不成功,所以需要移动构造进行优化,使资源转移而不是释放
return tmp;
}
6.2.1 移动构造
C++11中,将右值分为两种:纯右值和将亡值(自定义类型)。将亡值不再调用析构函数,希望调用移动构造进行资源转移。
两个func函数形成重载,左值引用调用拷贝构造,右值引用的10调用移动构造函数形成资源转换。
6.2.2 移动构造在传值返回方面的价值
没有移动构造:
有接收变量:
无接收变量:
移动构造:
6.2.3 移动赋值
有移动构造时,右值引用会调用移动构造而不是拷贝构造,移动构造会进行资源转移,提高效率。没有移动构造时,则调用拷贝构造进行深拷贝。
6.3 完美转发
完美转发:正确传递在传参的过程中保留对象原生类型属性
C++11通过std::forward函数来实现完美转发, 比如:
void Fun(int& x) { cout << "左值" << endl; }
void Fun(int&& x) { cout << "右值" << endl; }
void Fun(const int& x) { cout << "const左值" << endl; }
void Fun(const int&& x) { cout << "const右值" << endl; }
template<typename T>
void PerfectForward(T&& t) { Fun(std::forward<T>(t)); }
//不使用forward函数,则会把右值变成左值。
int main()
{
PerfectForward(10); //右值
int a;
PerfectForward(a); //左值
PerfectForward(std::move(a)); //右值
const int b = 10;
PerfectForward(b); //const左值
PerfectForward(std::move(b)); //const右值
return 0;
}
原文地址:https://www.jb51.cc/wenti/3284671.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。