C++11
目录
列表初始化
class Person {
public:
Person(const string& name, int age) :_name(name), _age(age) {}
private:
string _name;
int _age;
};
int main()
{
int i = { 1 };
vector<int> v = { 1,2,3 };
vector<int> v2{ 1,2,3 };
vector<int>{1, 2, 3};
//比较实用还是这种方式
Person* p_Arr = new Person[2]{ {"张三",31},{"李四",22} };
}
initializer_list<int> l1{ 1,2,3 };
initializer_list<string> l2{ "1","2","3" };
如果需要自定义类型支持花括号初始化,需要支持这样的构造函数。
template <class T>
class my_vector
{
my_vector(initializer_list<T> li) {
for (const T& x : li) {
this->push_back(x);
}
}
//...
//...
//...
//...
private:
};
int main()
{
my_vector<int> v = { 1,2,3,4 };
}
范围for
范围for本质还是由迭代器实现。需要注意,取出的元素是值传递,如不需要修改最好加上const &
int main()
{
vector<string> v = { "1","6" ,"54" ,"3" };
for (const string& i : v) {
cout << i;
}
}
STL中的新容器
array
一个存在栈上的静态数组,除了内部对下标访问做了检查,防止越界好像就没什么用。。
forward_list
底层是单链表,比list 少了尾插和尾插。唯一的优点就是少了一个存储前一个节点的指针节省了几字节。。
unordered_map/set
非常好用的容器,底层为hash表,但为什么到C++11 才支持。。
右值引用 移动语义
C++11 之前就有引用,应该称为左值引用,而C++11中新增了的右值引用语法特性,无论左值引用还是右值引用,都是给对象取别名。
什么是左值、右值
这些都是左值
int i = 1;
const int i2 = 1;
int* pa = new int[2]{ 1,2 };
左值的共同点:它们都可以被取地址,也基本可以修改(除了被const修饰)
右值可以是一个表达式,如:字面常量、表达式返回值,传值返回函数的返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。
这些都是右值
string func2()
{
string s1 = "12";
return s1;
}
int main()
{
int x, y;
x + y;
10;
1 + 2;
func2();
}
右值的共同点:它们都不能出现在赋值运算符的右边和被取地址,也不能被修改
int&& rr1 = 123;
rr1 = 2; //此时rr1已经是一个左值了,可以被修改
左值引用
左值引用小结:
左值引用只能引用左值,不能引用右值。
但是const左值引用既可引用左值,也可引用右值,例如:
const int i = 1;
在没有右值引用之前这种场景非常常用:
当模板类型为int、double 这种类型,就相当引用了右值
template <class T>
class my_vector
{
void push_back(const T& x) {
//....
}
}
左值引用的短板
当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。例如: 标准库中的to_sting函数
to_string 只能使用传值返回,传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。
右值引用
右值引用解决了上面左值引用的缺点,本质也是为了减少拷贝。
只有拷贝构造情况下,依旧会产生一次深拷贝。
但如果提供了移动构造,就不会产生深拷贝,同时有拷贝构造和移动构造的情况下,优先选择移动构造。
func函数中的临时对象出了作用域就即将销毁了,有些地方叫将亡值,使用移动构造相当于废物利用,直接将临时对象中的数据交换到新对象上来,减少了深拷贝,提高效率,这是右值引用的一个使用场景。