1.基本内置类型
1.1 wchar_t ,char16_t,char32_t为扩展字符集,和bool类型一样,均无signed/unsigned 修饰类型。
1.2 char的实现可能为unsigned或signed,具体由编译器决定,bool的实现并未明确,所以二者均不能用于存储数据。
1.3 char-8,short-int-16,long-32,long long-64最小的实现位宽(具体以系统而定),float-6,double-10,最小的实现有效数字位数,wchar_t -8,char16_t-16,char32_t-32固定大小。
1.4 若把将一个超出无符号类型范围的值赋给一个该无符号类型,结果为该值对无符号类型表示的总值取模。(这个话听着拗口,说白了便是把这个值在底层以无符号值予以对待),相反若将一个超出有符号类型范围的值赋给一个有符号类型,结果则未定义。从这里可以看出,在C++中若有符号和无符号混和参加运算时,有符号数必须会转为无符号数参加运算。这个可能造成很多误会。
1.5 字面值常量
1.5.1整形(int)字面值常量 30 /*十进制*/ 030/*八进制*/ 0x30/*十六进制*/
1.5.2浮点型(double)字面值常量,其可表示为一个小数或以e/E科学计数法表示的指数。如: 3.14 3.14E0, 0., 0e0, .001
1.5.3 字符型(char/char*)字面值常量,其用单引号表示一个字符,双引号表示字符串,编译器会在字符串后自动添加结束符。另还存在一些转义字符.如:\n;\r;\\;\";\';\t;\?等。
1.5.3可以直接指定字面值的类型。字符在前,数在后。 u -char16_t(UTF-16字符); U-char32_t(UTF-32字符), L-wchar_t, u8-char(注,仅用于字符串常量,UTF-8)
1.5.4 bool 类型的字面值为true或false,指针字面值为nullptr,当然对于一个nullptr的指针调用*,则会运行时报错;
2.变量
2.1 变量初始化
2.2对于内置类型,若定义于任何函数之外的变量被初始化为0,而在函数体之内则不会被初始化。
2.3C++11开始支持共四种初始化方式, int a = 0; int a = {0}; int a{0}; int a(0);其中{}的两种称为列表初始化方法,此方法有一个重要特点,当其中有信息丢失时,编译器会报错,这意味着从大类型转为小类型时的若{}则不能编译通过,小类型转为大类型则不会。例如 float f = {1.0}便会报错,原因由于1.0字面值是一个double,使用{}转为float便会丢失信息。
2.4 在嵌套作用域中的同名变量会覆盖外部作用域同名变量。
3.复合类型和const 限定符
C++中的三种类型修饰符 左引用 右引用 指针
3.0 右值 左值
所谓右值 便是即将消失的值或字面值, 如a +b,33等,对一个右值求内存地址是会报错的,左值便是可求地址的。
3.1左引用&
引用并非对象,只是对所引用的对象的一个别名,一旦绑定,便不可更改。所以由此我们可以推出,对于非const引用,必须初始化;必须类型相同;必须是一个对象(左值)。而对于const引用,意即不可通过此引用来修改原值,此此可以推出,其类型相同,直需基本类型相同即可,其值亦可为一个右值。
3.2 指针*
3..2.1指针本身便是一个对象,其指针必须在定义时赋初值。与引用不同,指针在其生命过程中可以指向多个对象。指针不能指向一个临时的右值。
3.2.2指针的值(地址)应属于下列4种状态之一,指向一个对象;指向紧邻对象所占空间的下一个位置;空指针;无效指针。
3.2.3 void* 可以用于存放任意类型对象地址,可以用于和指针比较,作为函数输入和输出,或者赋给另一个void*指针。但不能直接操作其所指对象。
3.2.4 指针是一个对象,所以const 在指针中有两个位置可出现。一是表明指针本身为const,另一是指针所指对象为const.此前一个意思指指针地址不可更改,后一个指所指对象不可再更改。const 所在位置* 前指指向对象,在后指地址本身。 const int * const ptr =nullptr;此处还有一个概念顶层const指的是指针地址的const,而底层const 表示指针所指对象const.
3.3右引用&&
3.4 复合类型的声明。对于各个修饰符复合的情况,应该注意,指针*,引用&,&& 并不是类型,所以对于 int* p,b 此种类型说明,b并不是一个int* ,仅是一个Int型。对于指针和引用的复用,由于引用并不是一个对象,所以指针是不能指向一个引用的,而指针是一个对象,所以可以建立一个引用指针。如int b =0; int *&a=&b;(此处a是一个引用,引用一个指针指向int,此指针用b初始化),或int* b =nullptr; int*&a=b;(同上)
4.constexpr 和常量表达式
常量表达式是指在编译时便具有明确的值的。如const类型从一个字面值赋值便是一个常量表达式,但对于一个返回const 限制符类型的函数便便不是一个常量表达式。
constexpr,则不存在这样的问题,constexpr函数同样是常量表达式。此处。对于于指针constexpr是一个顶层const限制,如constexpr int* a是指针a不可修改,而并非其所指对象。
5.处理类型
5.1 auto是以 类型推断.
5.1.1编译器通过初始值推断类型,所以auto 声明的变量必须初始化,也就是说auto 不能从一个字面类型推断,但可以在auto上添加const,如const auto a = 32;
5.1.2auto不能扩展推断,继承cv限定符。如unsigned a =4294967295; unsigned b =1;auto c=a+b,此时a+b会溢出,但auto并不能自动升级类型,依然会以unsigned来处理。
#include <iostream> #include <limits> using namespace std; int main() { cout << numeric_limits<unsigned int>::max()<<endl; unsigned int b = numeric_limits<unsigned int>::max(); auto a = b + 1; cout << a;// 输出 0 return 0; }
对于原类型中的cv限定符,auto的继承中全部不予处理 ,如:const int a =3;auto b =a;此时a的类型是int,而非const.,但需要注意对于 类似 auto b = &a,此时b是一个int * const类型
#include <iostream> #include <type_traits> #include <typeinfo> using namespace std; int main() { const int b = 0; auto a = b; auto ca =&b; cout << is_const<decltype(a)>::value<<endl; //输出 0 cout << typeid(ca).name();// 输出 int const*,此时的const是顶层的,即意味着对地址的,而并非所指对象的const return 0; }
5.1.3auto可以和指针与引用合用。切记一点的是指针是一种类型,而引用不是,所以auto可以推断出指针,不能推断出引用。所以若在auto上添加修饰符时,我们可以理解将auto转为相应的类型即可,另,若auto推断出是一个指针,再添加修饰符,此时并不‘*’并不迭加,而仅是合并,这便冗余忽略。如 int* a =nullptr; auto* b =a; 此时b是一个int*类型。
#include <iostream> #include <type_traits> #include <typeinfo> using namespace std; int main() { int* b = 0; auto val1 = b; cout << is_pointer<decltype(val1)>::value<<endl; // 输出 1,表明是一个指针 cout <<typeid(val1).name()<<endl;//输出 pi auto* val2 = b; cout << is_pointer<decltype(val2)>::value<<endl;// 输出1,表明是一个指针 cout<<typeid(val2).name()<<endl;//输出 pi //上面表明在多个指针冗余时,会忽略。 int v =0; int& rv =v; auto val3 =rv; cout <<is_lvalue_reference<decltype(val3)>::value<<endl;//输出 0,表明不是左值引用,即引用并不继承 auto& val4 =rv; cout <<is_lvalue_reference<decltype(val4)>::value<<endl;//输出1,表明是一个左值引用。 //auto* val5 = rv; //无法通过编译,指针不能指向 一个引用,参见上3.4 //cout<< is_lvalue_reference<decltype(val5)>::value<<endl; //cout << is_pointer<decltype(val5)>::value<<endl; auto& val6 = b; cout<< is_lvalue_reference<decltype(val6)>::value<<endl; //输出1,表明是一个左值引用 cout << is_pointer<decltype(val6)>::value<<endl;//输出0,表明不是一个指针 cout << typeid(val6).name();//输出Pi,表明其是一个引用,且引用的对象是一个指针 return 0; }
5.1.4 auto不能用于数组生成,若为数组,便会自动转为一个指针。如:
#include <iostream> #include <typeinfo> using namespace std; int main() { int b[5]={0}; auto a =b; cout << typeid(a).name(); //输出 pi return 0; }
5.2 typedef 中关于const 的问题。当typedef 一个复合类型时如 typedef char* pstring; const pstring cstr =0; 此处cstr前的const是指顶层const,即cstr不可更改。const pstring* ps此种亦是雷同的,即ps 指向此种顶层const char*指针。
5.3 decltype 类型推断。
5.3.1 decltype 可以通过表达式推断出类型。即并不是以后类型来推断的,所以对于引用(左值,右值)、const,数组,可以进行继承和推断的,但与auto对于冗余符合处理是一样,对冗余cv 修饰符会忽略的。void是不可推导的。
其推断规则为:
5.3.2 如果e 是一个没有带圆括号的标记符表达式或者类成员访问表达式,那么decltype(e) 就是e所命名的实体类型,此外,如果 e 是一个被重载的函数(这里指的函数类型,而非函数返回值),则会导致编译时错误。5.3.4 否则,假设e的类型是T,如果e是一个将亡值,那么decltype(e)的类型为T&&
5.3.5 否则,假设e 的类型是T,如果e是一个左值,则decltype(e)为T&,此处应注意,若不为原本的变量,凡是带有操作符的,如(),++,[],*包括字符串等,其结果后仍可赋埴的,均为左值。
5.3.6否则,假设e的类型为T,则decltype(e)为T;此时仅指为其基本类型。
#include <iostream> #include <type_traits> #include <typeinfo> using namespace std; int main() { int i =0; int arr[5] ={0}; int *ptr =arr; struct S { double d;} s; int& ri = i; void overloaded(int); void overloaded(char); int && rvalref(); const bool func(int); decltype(arr) var1; cout<<typeid(var1).name()<<endl; //int arr[5] decltype(ptr) var2; cout<<typeid(var2).name()<<endl;//int* decltype(s.d) var3; cout<<typeid(var3).name()<<endl;//double decltype(ri) var4 =i;//左值引用 cout<<typeid(var4).name()<<endl;//int cout<<is_lvalue_reference<decltype(ri)>::value<<endl;//1 //decltype(overloaded) var4; //第二种情况,直接为一个右值引用。 decltype(rvalref()) var5=1; cout << is_rvalue_reference<decltype(rvalref())>::value<<endl;//1 //此为第三种情况,结果为表达式,且为左值。 decltype(true?i :i) var7=i; cout << is_lvalue_reference<decltype(true?i:i)>::value<<endl;//1 decltype((i)) var8=i; cout<<is_lvalue_reference<decltype((i))>::value<<endl;//1 decltype(++i) var9=i; cout<<is_lvalue_reference<decltype(++i)>::value<<endl;//1 decltype(arr[3]) var10=i; cout<<is_lvalue_reference<decltype(arr[3])>::value<<endl;//1 decltype(*ptr) var11=i; cout<<is_lvalue_reference<decltype(*ptr)>::value<<endl;//1 decltype("lvar") var12="lvar"; cout<<is_lvalue_reference<decltype("lvar")>::value<<endl;//1 cout<<typeid(decltype("lvar")).name()<<endl;//字符串的类型是一个const char array[5]的引用,其中数组大小和字符串大小长度有关 //第四种情况,仅返回类型,无修饰符。 decltype(1L) var13; //本类型,除字符串字面常量为右值, cout<<typeid(decltype(1L)).name()<<endl;//long decltype(i++) var14; //返回类型,虽i++返回右值,为第四种情况,返回Int cout<<is_rvalue_reference<decltype(i++)>::value<<endl;//0 decltype(func(1)) var15 ;//返回类型bool,虽结果为const bool,此处若用vs2013编译时,会产生bug, //其产生const bool类型,不可更改变量值,但若用is_const<>去检测,结果却是1. cout<<typeid(decltype((func(1)))).name()<<endl;//bool cout<<is_const<decltype((func(1)))>::value<<endl;//0 return 0; }