C++ Primer 第2章 变量和基本类型(2)

2.3 复合类型

复合类型是指基于其他类型定义的类型,本节主要介绍引用和指针。
一条声明语句有一个基本数据类型和紧随其后的一个声明符列表组成。每个声明符命名了一个变量并指定该变量为与基本数据类型有关的某种类型。前面接触的声明语句,声明符就是变量名,变量类型就是声明的基本数据类型。其实还可以有更复杂的 声明符,它基于基本数据类型得到更复杂的类型,并把它指定给变量。

2.3.1 引用

引用为对象起了另一个名字,引用类型引用另一种类型。引用不是对象。
通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。

定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。

引用只能绑定在对象上,而不能与字面值或者某个表达式的结果绑定在一起,引用和它所引用的对象数据类型要相同。

2.3.2 指针

指针是“指向”另外一种类型的符合类型。与引用类似,指针也实现了对其他对象的间接访问。与引用的不同点,其一,指针本身就是一个对象,允许对指针赋值和拷贝,可以先后指向几个不同的对象;其二,指针无需在定义时赋初值。
定义指针类型的方法将声明符写成 星号d 的形式,其中d是变量名。

指针存放某个对象的地址,要想获取该地址,需要使用取地址符(&)

指针的类型要和它所指向的对象严格匹配,例如,不能把double型对象的地址赋给int型指针。
指针值
指针的值(即地址)应属于下列4种状态之一:
1.指向一个对象;
2.指向紧邻对象所占空间的下一个位置;
3.空指针,意味着指针没有指向任何对象;
4.无效指针,也就是上述情况之外的其他值。
试图拷贝或以其他方式访问无效指针的值都会引发错误,所以必须清楚任意给定的指针是否有效。尽管2.3形式的指针是有效的,但没有指向任何具体对象,所以试图访问此类指针对象的行为不被允许。
利用指针访问对象
如果指针指向了一个对象,则允许使用解引用符(操作符*)来访问对象。如果给解引用的结果赋值,实际上就是给指针所指的对象赋值。

空指针
空指针不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。生成空指针的方法:

建议初始化所有的指针,尽量等定义了对象之后再定义指向它的只占,如果不清楚指针应指向何处,就初始化为nullptr。
赋值和指针
一旦定义了引用,就无法令其再绑定到另外的对象,而给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。
只要指针拥有一个合法值,就能将它用在条件表达式中。对于两个类型相同的合法指针,可以用相等操作符和不等操作符来比较它们,结果为布尔类型。
void* 指针
void* 指针是一种特殊的指针类型,可用于存放任意对象的地址,我们对地址中到底是什么类型的对象并不了解,所以不能直接操作void* 指针所指的对象。

2.3.3 理解复合类型的声明

在同一条定义语句中,虽然基本数据类型只有一个,但是声明符的形式却可以不同。也就是说,一条定义语句中可能定义出不同类型的变量。

在定义语句中,类型修饰符(*或&)只作用于其后的一个变量,对其他变量不产生作用。涉及指针和引用的声明,一般两种写法,第一种是将修饰符和变量标识符写在一起,这种形式着重强调变量具有的复合类型;第二种是将修饰符和类型名写在一起,这种形式着重强调本次声明定义了一种复合类型。
指向指针的指针:指针是内存中的对象,像其他对象一样有自己的地址,因此允许把指针的地址再存放到另一个指针中。

指向指针的引用:引用本身不是对象,因此不能定义指向运用的指针,但存在对指针的引用。

面对一条比较复杂的指针或引用的声明语句时,从右向左阅读有助于弄清楚它的真实含义。

2.4 const限定符

有时需要定义一种变量,其值不能被改变。可以使用关键字const对变量的类型加以限定:const int bufSize = 512; 这样就把bufSize定义成了一个常量。因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。如果利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要。

默认状态下,const对象仅在文件内有效,当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。若确有必要在文件中共享,添加extern关键字。

2.4.1 const的引用

可以把引用绑定到const对象上,称为对常量的引用。对常量的引用不能被用作修改它所绑定的对象。

初始化和对const的引用
2.3.1节中提到,引用的类型必须与其所引用的对象的类型一致,但是有两个例外。第一种例外情况就是在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转化成引用的类型即可。尤其,允许一个常量引用绑定非常量的对象、字面值,甚至是一个表达式:

当一个常量引用被绑定到另外一种类型时,就会绑定到一个临时量对象,而非原来的对象,故不能通过引用对原来对象进行操作,所以非法。
对const的引用可能引用一个并非const的对象:常量引用仅对引用可参与的操作做出了限定,对于引用的本身是不是一个常量未做限定,对象也可以是个非常量,但不允许通过这个常量引用来修改这个非常量对象的值,允许通过其他途径改变它的值。

2.4.2 指针和const

类似于常量的引用,指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针(不能使用普通的指针):

2.3.2节提到,指向的类型必须与所指对象的类型一致。第一种例外情况是允许令一个指向常量的指针指向一个非常量对象。
const指针
指针是对象,允许把指针本身定为常量。常量指针必须初始化,初始化后其值不再改变。常量指针将一直指向某个对象不再改变。把*放在const前用以说明指针是一个常量。

指针本身是一个常量并不意味这不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型。curErr指向的是一个非常量整数,可以通过curErr修改errNumb的值,但pip是指向常量的常量指针,则不论pip所指的对象值还是pip自己存储的那个地址都不能改变。

2.4.3 顶层const

顶层const表示指针本身是个常量,底层const表示指针所指的对象是一个常量。更一般的,顶层const可以表示任意的对象是常量。

当执行对象的拷贝操作时,顶层const不受什么影响,而底层const的限制却不能忽视,拷入拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。

2.4.4 constexpr和常量表达式

常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定。

staff_size的数据类型只是普通的int而非const int,故不属于常量表达式;const int sz = get_size(),尽管sz是一个常量,但他的具体值知道运行时才能获取到,所以不是常量表达式。
constexpr变量
将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。

字面值类型
常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为“字面值类型”。算数类型、引用、指针都属于字面值类型。
constexpr指针的初始值必须是nullptr或0,或时存储于某个固定地址中的对象。定义在所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针;而函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。
如果在constexpr声明中定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关:

发布了16 篇原创文章 · 获赞 11 · 访问量 650

猜你喜欢

转载自blog.csdn.net/qq_42820853/article/details/104424441