1.auto类型推导
在早期版本中,关键字auto主要是用于声明具有自动存储期的局部变量。也就是说,是局部变量,除了static类型以外,其他变量(以“数据类型+变量名”的方式定义)都默认为具有自动存储期,所以auto关键字可有可无。
在C++11的版本中,删除了auto原本的功能,并进行了重新定义了。即C++11中的auto具有类型推导的功能。
静态类型就是在使用变量前需要先定义变量的数据类型
动态类型无需定义变量的数据类型
静态类型是在编译时进行类型检查,而动态类型是在运行时进行类型检查。
auto就相当于一个类型声明时的占位符,而不是一种“类型”的声明,在编译时期编译器会将auto替代成变量的实际类型。
auto的优势
对类型复类型变量声明时的简化代码.也避免一些在类型声明时的错误.最常见的就是迭代器的类型
std::vector<std::string> array; std::vector<std::string>::iterator it = array.begin(); auto it = array.begin();
当在需要double类型的时候避免定义为整数类型,使用auto,会自动推导.
不能将auto用于声明函数形参,貌似c++14中支持了.
auto限制
auto可以自动推断出指针,但是无法自动推断出引用
// 等价定义 auto* a = &x; auto a = &x // 费等价,第一个是值语义,第二个是引用 auto a=x; auto &a=x;
auto 会自动删除const(常量性),volatile(易失性)。
对于引用和指针,即auto*, auto&仍会保持const与volatile。
当定义多个变量在一行,所有变量的类型都要一致,且为第一个变量的类型,否则编译出错。
定义模板函数时,用于声明依赖模板参数的变量类型。就是对形参的运算,其结果在编译出来之前是未知的,可以使用auto来推到.类似的,依赖于模板参数的返回值也同样.
auto 变量必须在定义时初始化,类似于引用.
如果初始化表达式是引用,则去除引用语义。
如果初始化表达式为const或volatile(或者两者兼有),则除去const/volatile语义。
如果auto关键字带上&号,则不去除const语意。
初始化表达式为数组时,auto关键字推导类型为指针。
函数或者模板参数不能被声明为auto
auto仅仅是一个占位符,它并不是一个真正的类型,不能使用一些以类型为操作数的操作符,如sizeof或者typeid。
2 decltype 类型推导
decltype() 在有一个对象或是类型的时候,根据这个对象或是类型去推导出该类型,并用其创建对象.或是在啊该类型后继续加引用,或是指针,因此,需要对应的赋值需要与推导出的类型相符合
因此,其可以作为函数参数,因为他需要的对象,已经有类型了,而不是凭空产生.
类型推导是随着模板和泛型编程的广泛使用而引入的。在非泛型编程中,类型是明确的,而在模板与泛型编程中,类型是不明确的,它取决于传入的参数类型。
decltype与我前面讲到的auto还是有一些共同点的,如二者都是通过推导获得的类型来定义另外一个变量,再如二者都是在编译时进行类型推导。不过他们类型推导的方式有所不同,auto是通过初始化表达式推导出类型,而decltype是通过普通表达式的返回值推导出类型。
typeid 与 decltype
运行时类型识别(RTTI),
RTTI机制:为每个类型产生一个type_info类型数据,程序员可以在程序中使用typeid随时查询一个变量的类型,typeid就会返回变量相应的type_info数据,type_info的name成员可以返回类型的名字。在C++11中,增加了hash_code这个成员函数,返回该类型唯一的哈希值,以供程序员对变量类型随时进行比较。
直接对type_info.name进行字符串比较不就可以了么,为什么还要给每个类型一个哈希值?我认为,字符串比较的开销也是比较大的,如果用每个类型来对于一个哈希值,通过比较哈希值确定类型是否相同的方法,会比使用字符串比较的效率要高得多。
typeid(b).name() typeid(a).hash_code() == typeid(b).hash_code()
RTTI在运行时才确定出类型,而更多的需求是在编译时确定类型。并且,通常的程序是要使用推导出来的这种类型而不是单纯地识别它。
using size_t = decltype(sizeof(0));
decltype推导的四规则如下:
- 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,可能会导致编译错误;
- 假设e的类型是T,如果e是一个将亡值(xvalue), 那么decltype(e)为T&&
- 假设e的类型是T,如果e是一个左值,则decltype(e)为T&;
- 假设e的类型是个T, 则decltype(e)为T.
3,4的区别在于,3中传入的是一个运算后的结果.而4传入的是单个变量,没有运算
例如
int i=1; decltype((i)) a=0; // () 运算了,是 int& decltype(++i) b=1; // ++i是运算后的结果.推断为 int & decltype(i++) c =1; // 先推断 i ,再自增,因此是 int decltype(arr[1]) d=1; // 运算后的结果,因此是引用.
对于第一个,对于数组类型,decltype可以推导出 int [10] 完整的类型.
decltype能够“带走”表达式的cv限制符。不过,如果对象的实例的定义中有cv限制符时,其实例的成员不会继承const或volatile限制符。
# include <iostream> # include <type_traits> using namespace std; const int ic = 0; volatile int iv; struct S { int i; }; const S a = {0}; volatile S b; volatile S* p = &b; int main(void) { cout << is_const<decltype(ic)>::value << endl; // 1 cout << is_volatile<decltype(iv)>::value << endl; // 1 cout << is_const<decltype(a)>::value << endl; // 1 cout << is_volatile<decltype(b)>::value << endl; // 1 cout << is_const<decltype(a.i)>::value << endl; // 0 cout << is_volatile<decltype(p->i)>::value << endl; // 0 return 0; }
模板中,但返回值与参数有关的时候,比如是参数解引用后的类型,C++11引进了新语法——追踪返回类型,来声明和定义这样的函数
template<typename T1, typename T2> auto Sum(T1& t1, T2& t2)->decltype(t1+t2) { return t1+t2; }
3 范围for
基于范围的for循环,结合auto的关键字,程序员只需要知道“我在迭代地访问每一个元素”即可,而再也不必关心范围,如何迭代访问等细节。
如果范围for中使用auto推断类型,那么auto不会自动推导出引用类型,如需引用要加上&
4 nullptr
而典型的初始化指针通常有两种:0与NULL, 意在表明指针指向一个空的位置。
NULL其实是宏定义
// stddef.h # undef NULL # if define(_cplusplus) # define NULL 0 # else # define NULL ((void*)0) # endif
NULL既可被替换成整型0,也可以被替换成指针(void*)0。这样就可能会引发一些问题,如二义性:
在重载函数的时候,如果重载函数有接受int类型和接受void*类型的,那么传入NULL就会报错.
nullptr_t为nullptr的类型, 称为指针空值类型。
所有定义为nullptr_t类型的数据都是等价的,行为也是完全一致的
nullptr_t类型的数据可以隐式转换成任意一个指针类型。
nullptr_t类型数据不能转换成非指针类型,即使用reinterpret_cast()的方式也不可以实现转化;可以强转,但是报错提示精度损失,加上编译参数以后,强转会警告,值为0
nullptr_t类型的对象不适用于算术运算的表达式;
nullptr_t类型数据可以用于关系运算表达式,但仅能与nullptr_t类型数据或者是指针类型数据进行比较,当且仅当关系运算符为-=, <=, >=, 等时返回true。
null与(void*)0
nullptr是编译时期的常量,它的名字是一个编译时期的关键字,能够为编译器所识别,而(void*)0只是一个强制类型转化的表达式,其返回值也是一个void*的指针类型。
nullptr 能够隐式转化成指针,而(void*)0只能进行显示转化才能变成指针类型(c++11)。虽然在c++标准中(void*)类型的指针可以实现隐式转化。
6 __cplusplus
用与编译器判断使用的是什么编译器.
当使用c++的编译器,例如g++ clang++那么会自动定义这个宏.但是编译出来的程序又需要被c调用,或是调用c.则需要编译为c
所以使用该宏
代码开头:
#if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif
代码结尾:
#if defined(__cplusplus) || defined(c_plusplus) } #endif
7 对齐方式
c++11支持按照任意的方式进行对齐
alignas(8) char c[1024];
其中括号内也可以是一个类型,根据类型再推断出该类型的字节大小.
alignof可以返回对齐的大小
int i = alignof(a);
8 显示的默认或是删除的构造系列函数
9 委托构造函数
10 显示转换操作符
11 初始化列表
12 lambda函数