C++11新增标准讲解(上)
一,{}(列表初始化)
一切对象均可用 {} 初始化,例如下面代码,不管是自定义类型或者内置类型都可以使用{}初始化,还能将=省略。
需要注意的是当我们使用vector<Data>时不能用{}来初始化,可以发现下图使用{}初始化时,只有年份被初始化了,其他都是使用的缺省值。
如果需要初始化vector<Data>的话就需要使用std::initializer_list来初始化容器
class Data
{
public:Data(int y=1,int m=1,int d=1):_y(y),_m(m),_d(d){ }Data(const Data& data):_y(data._y),_m(data._m),_d(data._d){ }private:int _y;int _m;int _d;
};int main()
{Data d1 = { 2022,3,5 };//自定义类型Data d2{ 2022,3,5 };//自定义类型vector<Data> v1{ 2003,4,5 };//错误vector<Data> v2 = { 2003,4,7 };//错误vector<Data> v3 = { {2003,3,3},{2026,6,6} };//采用用隐式类型转换v3.push_back({2004,5,8});//隐式类型转return 0;
}
可以看到push_back也可以使用隐式类型转来插入数据
二,左值和右值
左值:能被取地址,能持久的存在,不是临时的,生命周期不是瞬间的
右值:不能被取地址,可以认为临时对象就是右值
int main()
{//左值int a = 2;string s1("ded");int* p = new int(0);cout << &s1 << endl;//右值int b = 3;int c = 4;(b + c);//生命周期就在这一行string("hhh");//匿名对象生命周期也只有这一行cout << &(b + c) << endl;//报错,右值不能取地址return 0;
}
三,左值引用与右值引用
//左值引用
int& a1 = a;
string& ss = s1;//右值引用
string&& sss = string("hhh");
int&& bc = (b + c);
可以看到左值引用使用一个&,而右值引用使用的是&&。
左值引用与右值引用就是给对应的对象取别名,同时右值引用还能延长对象的生命周期,而左值引用如果需要能延长对象生命周期的话就必须加const。
const int& r1 = (b + c);//如果想用左值引用给右值取别名,需要加上constint&& r2 = move(b);//如果想用右值引用给左值取别名,需要加上move()
move()是仿函数,作用是将目标对象强制转换为右值
需要注意的是变量表达式都是左值属性,也就意味着⼀个右值被右值引用绑定后,右值引用变量变 量表达式的属性是左值,如果需要保持原来的属性就需要用到完美转发。
类型折叠:我们在将右值或者左值当实参传入函数时,会发生类型折叠
传入右值
template<class T>
void func(T&& a)//T->int
{T b = 6;//T->int
}int main()
{int a = 2, b = 4;int&& x = (a + b);func(x);//传入的是右值,原本是int&&,传入函数后会被折叠成intreturn 0;
}
传入左值
template<class T>
void func(T&& a)//T&&->int&
{T b = 6;//T->int&,会报错,因为6是右值
}int main()
{int a = 2, b = 4;int&& x = (a + b);func(a);//传入的是左值,原本是int&,传入函数后会把后面的&&折叠掉return 0;
}
还需要知道的是&与&,&&折叠之后的属性还是左值,&&只有跟&&折叠时属性才是右值
四,移动构造与移动赋值
有了右值引用之后就出现了移动构造与移动赋值,大大减少了自定义类型传参时的拷贝
在没有引进之前,我们如果要接收一个函数返回的局部对象时就会先拷贝生成一个临时对象,然后再将临时对象拷贝给接收的对象,这样效率不是很高。
如果想要提高效率有两个方法,一种是将需要接收函数返回值的对象直接当函数参数,直接在函数内部改变s的值,还有一种是编译器的优化,编译器如果优化非常的好就可能将原本需要两次拷贝的直接优化成一次拷贝。
string addstring()
{string s1;//...return s1;//会拷贝成临时对象
}void addstring1(string& s)//
{//...直接对s进行操作,因为传入的是引用,里面改变了外面也会改变,就达到了减少拷贝}
int main()
{string s = string(s);return 0;
}
在引进之后,移动构造和移动赋值的第一个参数都是右值引用的类型,他的本质是要“窃取”引用的 右值对象的资源,而不是像拷贝构造和拷贝赋值那样去拷贝资源,从提高效率。