C++11中增加了一些新的特性,需要在vs2011(或Linux g++4.8,编译时需要加-std=c++11)之后的版本中才可进行语法解析。
1、nullptr常量
nullptr的出现是为了替换NULL,nullptr是一种特殊类型的字面值,可以被转换成任意的其他类型的指针。
传统的C++,NULL和0的意义是一样的。有些编译器会将NULL定义为((void*)0),有的编译器会将NULL定义为0.
NULL在重载中有可能会发生混乱,比如:
//当NULL被定义为((void*)0),fun(NULL)会调用下列函数
void fun(int*);
//当NULL被定义为0时,fun(NULL)会调用下列函数
void fun(int);
在C++11中引入nullptr来区分空指针和0. nullptr就是一个空指针。它的类型为nullptr_t,能隐式的转换为任何指针的类型。
2、自动类型推导
C++11中引入了auto和decltype这两个关键字来进行类型的推导。在讲解auto时,在加入for循环和typeid操作符的讲解。
auto
auto关键字可以通过初始值,进行类型的自动推导。
语法:auto 变量名 = 初始值 auto是根据初始值的类型来推导变量的类型的,所以初始值不能少。
基本用法:
auto b = 11; //auto推导为int
auto d; //编不过,无法推演
int x = 10;
auto a = &x; //推导出a的类型为int*,auto--->int*
auto* b = &x; //b--->int*,auto--->int,支持但不提倡这种写法
auto& c = x; //c--->int&,auto--->int
auto d = x;
cout << typeid(a).name() << endl; //输出变量a的类型
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
使用auto来推导迭代器:
//字典
map<string,string> dict{{"cat","猫"},{"word","单词"}};
map<string ,string>::iterator dit = dict.begin();
while(dit != dict.end())
{
cout << dict->first << ":" << dict->second << " ";
}
cout << endl;
//使用auto简化语法:更简洁
auto dit2 = dict.begin();
while(dit2 != dict.end())
{
cout << dict->first << ":" << dict->second << " ";
}
cout << endl;
//for循环和auto搭配只用
for(auto kv : dict)
cout << kv.first << ":" << kv.second << " ";
cout << endl;
const属性auto声明不能带来:如果使用指针更改了const变量x的值,auto进行类型推演的时候,只能根据编译器的优化,得到更改前的x的值。
const int x = 10; //x:10
int* p = (int*)&x;
*p = 20; //此时内存中x已经为20
cout << x << endl; //x=10,输出的是寄存器中的值
auto y = x; //y=10
int* pp = (int*)&y;
*pp = 30;
cout << y << endl; //30
x输出为10的要点:const修饰的变量,编译器进行优化,放在了寄存器中,编译器认为const修饰的变量是不会变的。但实际上可以通过指针进行改变,写为const只是对程序员的警告,这个变量是不能去改的。通过指针p将x改为了20,此时使用监视窗口发现x的值已经为20了,但编译器的优化在于,输出时输出的是寄存器中的const变量x的值,编译器认为这样是快的。
volatile属性auto声明可以带来:volatile关键声明寄存器变量,防止编译器进行优化。使用volatile声明的const变量,使用指针更改值后,编译器不进行优化,输出内存中的值,从而输出结果为20.
使用auto对volatile声明的变量进行推演的时候,得到的是x更改后的值。
volatile const int x = 10;
int* p = (int*)&x;
*p = 20;
cout << x << endl; //x=20,volatile声明的变量,防止编译器进行优化,输出的是内存中的值
auto y = x; //y = 20
cout << y << endl;
int* pp = (int*)&y;
*pp = 30;
cout << y << endl; //y = 30
auto不能推导的场景:
- auto不能作为函数的形参类型
- auto不能定义为类的非静态成员变量
- auto不能直接用来声明数组
- auto不能作为实例化的模板参数
auto的优缺点:
auto看起来简化了容器的迭代,是建立在非常熟悉容器结构的情况下。用auto接收一个函数的返回值,但不知道auto的类型,就不好进行遍历,比如,map有一first和second,如果不清楚map的数据结构,就不好进行输出。
auto加for很方便进行容器的遍历。但可读性变差,提高了维护成本,不懂C++语法的人,还要进行学习。
typeid操作符
typeid的作用是在动态运行时获取变量的类型名称,使用typeid(变量).name() ,输出变量的类型名称。比如,
int a = 10;
cout << typeid(a).name() << endl; //int
cout << typeid(1.1f).name() << endl; //float
//对于存在继承关系的类
class Base1 {};
class Derive1:public Base1 {};
Derive1 d1;
Base1& b1 = d1; //b1是Deriver1的一个引用
cout << typeid(b1).name() << endl; // 输出"class Base1",因为Derive1和Base1之间没有多态性
class Base2 {
virtual void fun( void ) {}
};
class Derive2:public Base2 { };
Derive2 d2;
Base2& b2 = d2; //b2是Derive2的一个引用
cout << typeid(b2).name() << endl; // 输出"class Derive2",因为Derive1和Base1之间有了多态性
注意:对非引用类型,typeid是在编译时期识别的,只有引用类型才会在运行时识别。
基于范围的for循环
作用:可以用来编历,修改数组,容器等,语法简单。可以和auto搭配使用。
缺点:违背传统C用法,带来可读性上的误区
用法:
//数组
int array = {1,2,3,4};
for(auto e : array) //赋值,将array中的元素依次取出来,赋值给e,e的改变不影响数组
{
cout << e << " ";
}
//更改数组元素:偶数乘2,这时候要使用&
int array[] = {1,2,3,4};
for(auto& e : array) //e的改变影响数组
{
if(e % 2 == 0)
e = e*2;
}
//容器:vector
vector<int> v{1,2,3,4}; //C++新的初始化的方式
for(auto i : v)
cout << i <<" ";
cout << endl;
decltype
decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。decltype可以对表达式的类型进行推导。
语法:decltype(表达式)
作用:用来获取表达式的类型,用此类型可以定义一个新的变量。
auto x = 10; //int
decltype(x) y; //int
auto a = 1;
auto b = 2;
decltype(a+b) c; // int c
3、初始化列表
未完待续!