#include
using namespace std;
struct A
{
int _x;
int _y;
};
int main()
{
// 三种方式等价,并且可以省略=
int x = 1;
int y = { 2 };
int z{ 3 };
return 0;
}
{}按声明顺序初始化类成员变量
A p{ 1,2 };
cout << p._x; //1
cout << p._y; //2
本质上是调用构造函数来初始化,而且在没写构造函数的情况下,还不能用A p(1,2)来初始化。
// 初始化对象
int* ptr = new int[3]{ 1,2,3 };
A* ptr1 = new A[2]{ {1,2},{2,3} };
本质上可以看成多参数隐式类型转换。
struct A
{
string _x;
string _y;
};
A p{ "hello","hi" };
auto il = { 10,20,30 };
cout << typeid(il).name();
initalizer_list有两个变量_start指向开头的变量,_finish指向末尾变量
int a[] = { 1,2,3 };
//底层是调用initlize_list的构造函数
vector的C++11支持接收initializer_list列表
用initializer_list来初始化构造函数
vector v1 = { 1,2,3,4,5 };
decltype(x) y;
//y的类型和x的类型一样
int* p=Null; // 0
int*p1 = nullptr// 0指针
array a1;
int arr[10];
sizeof(a1);
sizeof(arr);
两个方式差不多,唯一的区别是int arr[]对越界检查更加严格。
forward_list fl;
cbegin()返回const的迭代器
左值:可以获取地址,一般可以对他赋值,左边可以出现赋值符号左边。
int a=1; // a为左值,能取地址能赋值
const int b=1 // b虽然不能赋值,但能取地址
右值:不能取地址,如函数返回值,字面量等。右值不能在赋值符号右边
double x=1.5,y=1.5;
double z=x+y// x+y为表达式值,是右值
10// 也为右值
"xxxx"//为右值,能取地址是因为表达式返回首元素地址
fun(); //函数返回值也为右值 fun()=1;错误
内置类型的右值叫做纯右值
int add(int x, int y)
{
return x+y;
}
自定义类型的右值叫将亡值, str为将亡值
string func()
{
string str;
return str
}
左值引用就是给左值取别名,右值引用就是给右值取别名
int a = 0;
int& r1 = a; //左值
int&& r2 = 5; // 右值
const修饰的左值引用能接收右值
右值引用可以修饰左值加上move()
const int& r2 = 10;
int a=0;
int&& r3=move(a);
但是虽然两个方法都能接收右值,但时右引用接收的优先级更高
void func(const int& r)
{
cout << "void func(const int& r)" << endl;
}
void func(int&& r) //传右值优先级更高
{
cout << "void func(int&& r) " << endl;
}
fun(2);
String func()
{
string ret;
return ret;
}
int main()
{
string str=func();
return 0;
}
在上面的代码中str深拷贝生成临时变量,临时变量再赋值给str,至少两次深拷贝。
第一次深拷贝生成临时变量,第二次=赋值重载符临时拷贝变量。
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 深拷贝" << endl;
string tmp(s._str); //创建新的对象
swap(tmp);
}
在上面两次的构造中实际上都是利用const string& s 来接收右值,即用左应用来接收右值。
第一次的拷贝构造的左引用指向的是ret值,第二次左引用指向的是临时变量的值。
为什么拷贝构造要创建新的对象,而移动构造只用交换指针。(不太恰当的比喻)
因为如果拷贝构造相当于接收一个存储值的变量(ret 变量 和 临时变量),如果直接交换指针指向内容,变量空间释放,会使得指针变成野指针。
而移动构造相当于接收一个值。
//拷贝构造 int fun() { int a=1; return a; } const int& b=a;// 没有创建临时变量(没有创建新对象),a的空间被释放。 int fun() { return 1; } int&& b=1; // 可以不创建临时变量。
由此我们理解到右引用相当于跳过接收变量,直接接收值。特别是对于对象之类的。
优化方法我们第二次拷贝赋值时,用右值引用接收它,再交互指针,指针指向空间交换,s临时变量会销毁。
// 移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动语义" << endl;
swap(s);
return *this;
}
编译器进一步优化
编译器会进一步优化成剩下一步移动构造>
右值引用还能在数据结构对象调用构造函数时减少空间开辟。
上图中初始化Node的时候还是会调用拷贝构造,下面中直接把值来过来初始化,调用移动构造。
右值引用的属性默认是左值
// 0是右值,a是左值,可以取地址
int&& a = 0;
cout << &a;
万能引用: 类型&& 万能引用,既可以接收左值,也可以接收右值
实参左值,它就是左值引用(引用折叠),实参右值,就是右值引用。
template
void PerfectForward(T&& t)
{
Fun(t);
}
右值引用的类型是左值
void Fun(int& t)
{
cout << "左值引用" << endl;
}
void Fun(int&& t)
{
cout << "右值引用" << endl;
}
template
void PerfectForward(T&& t)
{
Fun(t);
}
int main()
{
int&& a = 1;
PerfectForward(1);
return 0;
}
输出"左值引用":右值引用会被编译器处理识别成左值。
Forward使得右值引用类型保持右值属性。
void PerfectForward(T&& t)
{
Fun(forward(t));
}
完美转发的引用:
void PushBack(T&& x)
{
//Insert(_head, x);
Insert(_head, std::forward(x));
}
auto func = [](int x, int y)->int {return x + y; };
func本质上是个对象,和仿函数的作用相似。返回值类型可以省略。
struct greater1
{
bool operator()(int x, int y)
{
return x < y;
}
};
int main()
{
// 确定谁在前,x>y 说明当x大的时候,返回true,第一个参数在第二个参数前面,大的参数在前面
auto func = [](int x, int y) {return x v = { 1,23,3,31,4,6,7,8,14,56};
sort(v.begin(), v.end(), func);
sort(v.begin(), v.end(), greater1());
sort(v.begin(), v.end(), [](int x, int y) {return x < y; });
for (auto e : v)
{
cout << e << " ";
}
return 0;
}
auto swap2 = [a, b]()mutable {
//a, b不能修改 传值捕捉 a的属性是const不能修改的
// a,b改变不影响外面相当于a,b的拷贝
int tmp = a;
a = b;
b = tmp;
};
int main()
{
int a=0;
int b=0;
auto swap2 = [=]()mutable {
int tmp = a;
a = b;
b = tmp;
};
return 0;
}
auto swap3 = [&a, &b]() {
// 引用捕捉
double tmp = a;
a = b;
b = tmp;
};
引用捕捉:捕捉所有的引用对象
int main()
{
int a=0;
int b=0;
auto swap2 = [&](){
int tmp = a;
a = b;
b = tmp;
};
return 0;
}
引用捕捉所有对象除了a,a是传值捕捉
auto func1 = [&, aa]{};
auto add = [](int x, int y)->int {return x + y; };
auto func2 = [add](int& x, int& y)
{
add(x, y);
};
lambda表达式内可以直接调用全局函数不能调用局部函数。