类型限定符
const
限定符
const
变量:定义一种变量,值不能被改变,试图修改会报错
形式: const int i = 10;
基本类型和const
-
基本类型的非
const
和const
变相都能相互转化和赋值,和const
无关int i = 42; const int ci = i; //正确 int j = ci; //正确 bool bi = i; //正确,bi是true
-
默认情况下,
const
对象仅在文件内有效若想跨文件访问,可以声明为
extern
引用和const
-
被
const
修饰的引用常量,不能通过引用修改它绑定的对象const int ci = 42; const int &r1 = ci; //正确,引用和其绑定对象都是常量 r1 = 42; //错误,r1是对常量的引用
-
const
的引用常量不能给非const
变量赋值假设可以,那么久可以通过非
const
变量改变const
引用的对象的值了,是不正确的int &r2 = ci; //错误
-
非
const
变量可以给const
引用常量赋值int i = 42; int &r1 = i; //正确
虽然不能通过r1改变i的值,但是i自己还是可以改变的,i改变后,r1也就跟着变化了
int i = 10; const int &ri = i; i = 20; > > 输出:i是20 , ri也是20
指针和const
-
和引用一样,指针也可以指向常量或者非常量
- 可以用
const
常量给const
指针赋值 - 也可以用非
const
变量给const
指针赋值 - 不可用
const
常量给非const
指针赋值 const
指针指向一个非const
变量后,非const
变量自身也是可以修改的
- 可以用
-
与引用不同的是,指针是对象,引用不是
所以存在指针本身是常量,称为常量指针,常量指针必须初始化,初始化后,就不能再改变
-
const int *p = xx;
p是一个指向常量的指针,p可变,*p不可变
-
int * const p = xx;
p是一个常量指针,p不可变,*p可变
-
const int * const p = xx;
p是一个指向常量的常量指针,p不可变,*p也不可变
-
理解
const
的含义,也可以从右向左理解 离p最近的有const
,那么就是一个常量指针,没有就是指针变量 再往前,最开始有const
,那么表示p指向的常量不可变;没有就表示指向的变量可变
-
顶层const
和底层const
-
顶层
const
:const
修饰的变量不可变int i = 0; int *const p1 = &1; //顶层const,p1不可变 const int ci = 42; //顶层const,ci不可变
顶层
const
在数据拷贝时不受影响const int *const p3 = p2; i = ci; //ci是顶层const,无影响 p2 = p3; //p3顶层const部分不受影响
-
底层
const
:const
修饰的指针变量指向的数据不可变const int *p2 = &ci; //底层const,p2指向的变量不可变 const int &i = ci; //底层const,引用不可变
底层
const
的限制不能被忽视。指向拷贝时,拷入和拷出的对象必须具有相同的底层const
资格。一般的,非常量可以转换成常量,反之不行
int *p = p3; //错误,p3包含底层const,p不包含 p2 = p3; //p2和p3都包含底层const
constexpr
和常量表达式
-
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式
字面值属于常量表达式,用常量表达式初始化的
const
对象也是常量表达式const int max_files - 20; //max_files是常量表达式 const int limit = max_files + 1; //limit是常量表达式 const int sz = get_size(); //sz不是常量表达式,因为具体值直到运行时才能获取到
-
使用
constexpr
申明变量,以便编译器来验证变量是否是一个常量表达式声明为
constexpr
的变量一定是一个常量,而且必须用常量表达式初始化constexpr int mf = 20; //20是常量表达式 constexpr int limit = mf +1; //mf+1是常量表达式 constexpr int sz = size(); //只有size是一个constexptr函数时才正确
字面值类型
-
算数类型、引用和指针都属于字面值类型
-
自定义的类都不属于字面值类型,不能定义成
constexpr
-
尽管指针和引用都能定义成
constexpr
,但初始值却受到严格限制一个
constexpr
指针的初始值必须是nullptr
或者0,或者是存储于某个固定地址中的对象 -
函数体内定义的变量一般来说并非存放在固定地址中,因此
constexpr
指针不能指向这样的变量定义于所有函数体之外的对象其地址固定不变,能用来初始化
constexpr
指针
指针和constexpr
-
在
constexpr
声明中如果定义了一个指针,限定符constexpr
仅对指针有效,与指针所指的对象无关:const int *p = nullptr; // p是一个指向整型常量的指针 constexpr int *q = nullptr; //q是一个指向整型的常量指针
-
与其他常量指针类似,
constexpr
指针既可以指向常量也可以指向一个非常量int j = 0; constexpr int i = 42; constexpr const int *p = &i; //指向常量,p是一个常量指针 constexpr int *P1 = &j; //指向非常量,p1也是常量指针
处理类型
类型别名
-
形式
-
传统的使用
typedef
typedef double wages; //给double定义wages的别名 wages *p = nullptr; //使用别名定义指针数据
-
新方式,使用别名声明
using
用关键字
using
作为别名声明的开始,其后紧跟别名和等号,其作用是把等号左侧的名字规定成等号右侧类型的别名using SI = double; // SI是double的同义词
-
-
指针、常量和类型别名
-
类型别名使用在指针上,正好也加上了
const
限定,那么现在这个const
指向的是什么?typedef char *pstring; //pstring实际是char *的别名 const pstring cstr = 0; //cstr是指向char的常量指针,即cstr不可变 const pstring *ps; // ps是指向 char的常量指针 的指针
pstring
实际是char *
的别名,而const
是对给定类型的修饰。即const pstring
就指向char
的常量指针,而非指向char
常量的指针也就说
pstring
是指针类型,那么const pstring
中的const
就是修饰指针的,也就是常量指针 -
不能使用原始类型替换掉别名理解含义
如 const char * cstr = 0;
这是错误的
pstring
是指针类型。可是用char*
重写了声明语句后,数据类型就变成了char
,*
成为了声明符的一部分。这样改写的结果是,const char
成了基本数据类型。 前后两种声明含义截然不同,前者声明了一个指向char的常量指针,改写后的形式则声明了一个指向const char
的指针
-
auto
类型
自动类型,让编译器去分析表达式所属的类型
-
auto
是让编译器通过初始值来推算变量类型,所以,auto
定义的变量必须有初始值auto i = 0; //auto是int
-
引用作为
auto
变量的初始值时,此时变量的类型就是引用对象的类型int i = 0 ; &r = i; auto a = r; //a是int类型
-
在同一行定义多个变量时
符号
&
和*
只从属于某个声明符,而非基本数据类型的一部分,所以auto想要修饰引用或者指针也必须加上&
或者*
auto i = 0, &ri = i, *p = &i; //i是int 类型 //ri就是i的引用 //p指向int类型i的数据
-
const
和auto
-
auto
会忽略顶层const
如果需要顶层
const
,则需要明确加上顶层const
int i =0; const int ci = i; &cr = ci; auto b = ci; //b是一个int数据,顶层const被忽略 const auto b2 = ci; //b2是一个int常量,明确机上顶层const
-
auto
会保留底层const
auto c = &ci; //c是指向整型常量的指针 , 对常来那个取地址,是一种底层const
-
decltype
类型指示符
decltype
,它的作用是选择并返回操作数的数据类型 在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值
-
形式
decltype( f() ) sum = x; //sum的类型是函数f的返回值类型
-
与
auto
的区别decltype
使用的表达式是一个变量,则类型就是该变量的类型,也会保留顶层const
decltype
的结果类型与表达式形式密切相关
-
总结
-
decltype( (variable) )
永远是引用,注意,是两个括号int i = 0; decltype((i)) d = i; //d是int&,必须初始化
-
decltype(引用)
是引用decltype(&i) d = i; //d是int&类型
-
decltype(*p)
是引用int p = &i; decltype(p) d = i; //d是int &类型
-
decltype(引用作为表达式的一部分)
是引用的变量的数据类型decltype( i+0 ) d ; //d是int类型
-
decltype(基本类型)
是数据类型decltype(i) d ; //d是int类型
-