// C++ 11 新特性:
// 1、 内置类型列表初始化
// 列表初始化可以在{}之前使用=,其效果与不使用=没有区别;
#if 0
int main() {
//内置类型变量
int x1 = { 10 };
int x2{ 10 };
int x3 = 1 + 2;
int x4 = { 2 + 3 };
int x5{ 3 + 4 };
// 内置类型数组
int arr[5]{ 1,2,3,4,5 };
int arr2[]{ 1,1,2,4 };
// 动态数组
int* arr = new int[5]{ 1,2,3,4,5 };
// 标准容器
vector v{ 1,2,3,4,5 };
map m{ {1,2},{2,3},{3,4} };
return 0;
}
#endif
// 自定义类型的列表初始化
#if 0
class Point {
public:
Point(int x = 0, int y = 0):_x(x),_y(y){}
private:
int _x;
int _y;
};
// 单个对象的初始化列表
int main() {
Point p{ 1,2 };
return 0;
}
// 多个对象的初始化列表
// 多个对象想要只吃列表初始化,需要该类(模板类)带友initializer_list类型参数的构造函数即可
// 注意: initializer_list 是系统自定义的类模板,该类模板主要友三个方法begin(),end()迭代器以及获取区间中元素个数的方法size()
#include
template
class Vector {
public:
Vector(initializer_list l) :_capacity(l.size()), _size(0) {
_array = new T[_capacity];
for (auto e : l) {
_array[_size++] = e;
}
}
private:
T* _array;
size_t _capacity;
size_t _size;
};
#endif
// decltype : 类型推导
// typeid 只能查看类型不能用其结果类定义类型;
// dynamic_cast 只能应用于含有虚函数的继承体系中;
// decltype 是根据表达式的实际类型推演出变量所用的类型。
// 比如 1. 推导表达式类型作为变量的定义类型
#if 0
void declt() {
int a = 1;
int b = 2;
// 用decltyte推演a+b的实际类型,作为c的类型
decltype(a + b) c;
cout << typeid(c).name() << endl;
return 0;
}
#endif
// 推演函数返回值的类型
#if 0
#include
#include
#include
using std::cout;
using std::endl;
using std::string;
void* GetMemory(size_t size) {
return malloc(size);
}
int main() {
// 没有带参数,推导函数的类型
cout << typeid(decltype(GetMemory)).name() << endl;
// 如果带友参数列表推导的是函数返回值的类型,注意这里只做推演,不会执行函数
string name = typeid(decltype(GetMemory(8))).name();
cout << name << endl;
return 0;
}
#endif
// 显示缺省函数
// 在C++ 11 中,可以在默认定义或者声明上加上=default, 从而显示的指示编译器生成该函数的默认版本; 用=default 修饰的函数称为显示缺省函数
// 删除默认函数
// 如果想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且不给定义,这样只要其他人想要调用就会报错。
// 在C++11 中更简单,只需要在该函数的声明上加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete 修饰的函数为删除函数.
#if 0
// 左值与右值:
// 左值与右值是C语言中的概念,但在C标准中没有给出严格的区分方式,一般认为: 客户以放在 = 左边的或者能够 取地址的称为左值,
// 只能放在 = 右边的,或者不能取地址的称为右值;
#include
using std::cout;
using std::endl;
int g_a = 10;
int& GetG_A() {
return g_a;
}
int main() {
int a = 10;
int b = 20;
// a 和 b 都是左值,b既可以在 =的左侧 也可以在 =的右侧,
// 说明:左值既可以放在 = 的左侧,也可以放在 = 的右侧;
a = b;
b = a;
const int c = 30;
// c = a; 编译失败,c为常量,只读不允许被修改
// 因为可以对c取地址,因此c严格来说不算是左值
cout << &c << endl;
// b +1 = 20 编译失败:因为b+1的结果是一个临时变量,没有具体名称,也不能取地址,因此为右值
GetG_A() = 100;
return 0;
}
关于左值与右值的区分:
1. 普通类型变量,因为有名字,可以取地址,都认为是左值;
2. const 修饰的常量,不可以修改,只读类型的,理论应该按照右值来对待,但是应为器可以取地址(如果只是 const 类型常量的定义
编译器不给其开辟空间,如果对该常量取地址是,编译器才为其开辟空间),C++11默认其为左值。
3. 如果表达式的运行结果是一个临时变量或者对象,认为是右值。
4. 如果表达式运行结果或单个变量是一个引用则认为是左值。
引用与右值的比较:
普通引用只能引用左值,不能饮用右值,const 引用既可以引用左值,也可以引用右值。
int val() {
// 普通引用只能引用左值,不能引用右值
int a = 10;
int& ra1 = a;
// int& ra2 = 10; 编译错误,因为10是右值
const int& ra3 = 10;
const int& ra4 = a;
return 0;
}
C++ 中的右值引用,只能引用右值,一般情况不能引用左值;
void test() {
// 10 纯右值,本来只是一个符号,没有具体空间,
// 右值引用r1在定义过程中,编译器产生了一个临时变量,r1实际引用的是临时变量;
int&& r1 = 10;
r1 = 100;
int a = 10;
// int&& r2 = a; 编译失败、右值引用不能引用左值;
}
移动语义: 将一个对象中资源移动到另外一个对象中去。
当需要用右值引用引用一个左值是时,可以通过std::move()函数将左值转化为右值。
它并不搬移任何东西,唯一的功能就是将一个左值强制转换为右值引用,让后实现移动语义。
完美转发是指在函数模板中,完全按照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。(完美的转发的接受转发的函数是不会察觉到有中间商的)
#endif
// lambda 表达式
// [捕捉列表](参数列表)mutable->返回值类型{statement}
// [capture-list] : 捕捉列表,该列表出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,
// 捕捉列表能够捕捉上下文中变量提供给lambda函数使用。
// (parameters): 参数列表。与普通函数的参数列表一直,如果不需要可以连同() 一起省略。
// mutable : 默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略。
// ->return-type : 返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类能被推导也可以省略。
// {statement} :函数体。 在函数体内,可以使用其参数外,还可以使用捕获到的变量。
// 注意: lambda 函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。
// [var] : 表示值传递的方式捕捉变量的var
// [=] : 表示值传递方式补货所有父作用域中的变量(包括this)。
// [&var] : 表示引用传递捕捉变量var
// [&] : 表示引用传递捕捉所有父作用域的变量(包括this)。
// [this]: 表示值传递的方式捕捉当前this指针。
// a. 父作用域是指包含lambda函数的语句块。
// b. 语法上捕捉列表可以由多个捕捉项组成,并以 , 分割。
// c. 捕捉列表不允许变量重复传递,否则就会导致编译错误。
// d. 在块作用域以外的lambda函数捕捉列表必须为空。
// e. 在块作用域中的lambda函数仅能捕捉父作用域中的局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
// f. lambda表达式之间不能相互赋值,即使看起来类型相同。
// 函数对象:又称仿函数,既可以像函数一样使用的对象,就是在类中重载了operator()运算符的类对象。