1.初始化列表
在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
struct Fun
{
int x;
int y;
};
struct Date
{
Date(int _year, int _month, int _day)
:year( _year)
,month(_month)
,day(_day)
{
}
int year = 2005;
int month = 01;
int day = 17;
};
int main()
{
Fun st = { 1,2 };//一切都是初始化列表
int a = 10;
int b = { 10 };
int arr[10] = { 0 };
Date d = { 2024,8,26 };//这个是参数一个一个对应,用的是初始化列表
//先隐式类型转换,产生临时对象,再进行拷贝构造
vector<int> v = { 1,2,3,4,5,6,7 };//这个初始化是 利用的是initializer_list
}
2.声明
2.1 auto 自动推出数据类型
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局 部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将 其用于实现自动类型腿断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初 始化值的类型。
2.2 decltype 和auto 有点类似,也可以进行推到数据类型,主要的是将变量的类型声明为表达式指定的类型
3 nullptr
在c++11中,c语言一直使用的NULL,被记录成了0,会出现匹配错误的问题,会匹配到整型,第二个是NULL会被记录成((void*)0),这样也是不行的,因为c++中void*转换成其它类型需要强转
4范围for
5.右值引用和移动语义
5.1 左值引用和右值引用 传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们 之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。 什么是左值?什么是左值引用? 左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋 值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
左值:
右值:
左值和右值引用的互相转换:
int main()
{
//左值
int* p = new int(0);
int b = 1;//左值有的可以修改
const int c = b;//此时的左值不可以修改
*p = 10;
string s("111111");
cout << &c << endl;
cout << &s[0] << endl;
//右值
int x = 10,y=20;
10;
x + y;
Fun(x);
//左值的引用
int*& rx1 = p;
int& rx2 = b;
const int& rx3 = c;
//右值的引用
int&& lx1= 10;
int&& lx2 = x + y;
int&& lx3 = Fun(x);
//左值的引用 向右值
//左值引用引用给右值取别名:不能直接引用,但是const 左值引用可以
const int& rxx1 = 10;//右值都具有常性
const int& rxx2 = x + y;
const int& rxx3 = Fun(x);
//右值引用 左值
//右值引用引用给左值取别名:不能直接引用,但是move(左值)以后右值引用可以引用
int*&& lxx1 = move(p);
int&& lxx2 = move(b);
const int&& lxx3 = move(c);
}
5.2:移动语义:移动拷贝和移动赋值
我们用模拟实现string 来举一个例子
!:右值的引用它的本质上是左值,为什么呢,就拿移动拷贝来说 我们要举行swap转换,用swap转换各自的资源,右值一般是临时对象,常量,都具有const的性质,如果右值引用本质是右值,那么我们如何用swap来转换资源呢?为什么用swap转换资源,右值是将亡值,我们用swap转换资源,那么本身的那块拿去析构,剩下的那块也不用开辟空间进行拷贝,一举两得。
接下来我们模拟实现一下list 的移动语义:
我们可以看到在c++11中像push_back, insert 等等函数具有了右值版本,我们只需要稍加改造
<1>我们可以看到 在push_back中,传给insert的时候,要进行move一下,因为右值的引用本质是左值,如果直接穿的话,那么insert的时候就会匹配到左值insert
<2> 在insert new开辟空间的时候,我们也要move一下,这是因为我们在new 开辟空间会调用此时结点的构造函数,那么构造函数我们也要写右值版本的,既然要匹配到右值版本的构造函数,我们就要传右值。
6.引用折叠 和 完美转发
引用折叠:在函数模板中,我们传参的时候,不知道是左值或者右值,我们可以使用引用折叠,自动让他识别,让编译器帮助我们实现两种版本的函数
完美转化:我们用了引用折叠,那么我们再次调用别的函数的时候,就像上面,假如是右值,传参的时候匹配到了左值该怎么办,我们就要用forwar函数,让他自动识别帮助我们传参
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }
template<class T>
void PerfectForward(T&& s)
{
Fun(forward<T>(s));//自动推导引用的类型
}
int main()
{
PerfectForward(10); // 右值
int a;
PerfectForward(a); // 左值
PerfectForward(std::move(a)); // 右值
const int b = 8;
PerfectForward(b); // const 左值
PerfectForward(std::move(b)); // const 右值
return 0;
}
7 可变参数模板
C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比 C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改 进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。
我们打印一下参数包:
void Print()
{
cout << "end" << endl;
}
template<class T, class ...Args>
void Print(T&& x, Args... args)//第一种方式 递归打印函数参数包
{
cout << x << " ";
Print(args...);
}
template<class ...Args>//函数参数包
void ShowList(Args... args)
{
Print(args...);
}
template <class ...Args>
void ShowList(Args... args)//第二种 数组里面打印
{
int arr[] = { (cout << (args) << " ", 0)... };//此时的逗号表达式是为了躲避编译器的检查,cout的返回不能隐式转换
//但是存储数组的时候,是一个都好表达式,都会被判定成int 类型,所以可以进行打印
cout << endl;
}
int main()
{
ShowList(1, 'A', std::string("sort"));
return 0;
}
递归: 它属于那种一次递归,减少一个参数那种。然后直到没有参数的时候,因为它这个属于编译时递归,我们不能用if return 的方式结束,我们用一个没有函数模板,没有参数的函数来结束。
第二种:属于再在数组中把参数一个一个让编译器实现,然后打印。