C++ Primer 阅读笔记(二)

简介: C++ Primer 阅读笔记(二)

2.4 const限定符

const 修改的变量,其值无法更改


const 对象创建后其值无法更改,所以必须初始化


1 初始化和const

const 类型对象只能执行不改变其内容的操作。例,利用 const 对象去初始化另外一个对象(因为拷贝一个对象的值并不会改变它,拷贝完成的新对象和原来对象无关)

int i =42;
const int ci = i;  //正确:i的值被拷贝给了 ci 
int j = ci;      //正确:ci的值被拷贝给了 j

拷贝一个对象的值并不会改变它,一旦拷贝完成,新的对象就和原来的对象没什么关 系了。


2 const 对象默认仅在文件内有效

初始化方式定义一个 const 对象时,编译器将在编译过程中把用到该变量的地方都替换成对应值


为了多文件定义 const 变量,且避免对同一变量重复定义。const 对象默认仅在文件内有效。程序多个文件中同名的 const变量,等于多个文件分別定义了独立变量


多个文件之间共享 const 对象,须在变量定义前添加 extern


// file_l.cc 定义并初始化了一个常量,该常量能被其它文件访问
extern const int bufSize = fen();
// file_l.h  与file_l. cc中定义的 bufSize是同一个
extern const int bufSize;


file_l.cc 定义并初始化了 bufSize,因为其是常量,需用 extern 修饰使其被其他文件使用


file_l.h 中声明 也有extern 修饰(限定),作用是指明 bufSize 并非本文件所定义,它的定义在别处


2.4节练习

【练习2.26】
下面哪些句子是合法的?如果有不合法的句子,请说明为什么?
(a) const int buf;    // 不合法,需初始化
(c) const int sz = ent; // 合法 初始化时与 const 无关
(b) int ent =0;  // 合法
(d) ++cnt; ++sz;  // 不合法,const限定对象其值不可修改

2.4.1 const 的引用

把引用绑定到 const 对象上,称为对常量的引用,简称常量引用


与普通引用不同,常量引用不能修改它绑定的对象


const int ci =1024;
const int &rl = ci; //正确:引用及其对应的对象都是常量
rl =42;    //错误:rl是对常量的引用
int &r2 = ci;   //错误:让非常量引用指向常量对象

非常量引用无法指向常量对象,因初始化合法,便可通过其来改变它引用对象值,这显然不正确


1 初始化和对 const 的引用

除了两个例外,引用类型与引用对象类型一致


第一种情况:初始化常量引用时,允许用任意表达式作为初始值,只要该表达式结果能转换成引用类型即可


int i =42;
//将int常量引用绑定到普通int对象上
const int  &rl = i;   
const int  &r2 =42;      //正确:rl是一个常量引用
const int  &r3 = rl * 2;  //正确:r3是一个常量引用
int &r4 =  rl * 2;       //错误:r4是一个普通的非常量引用

【理解例外情况】


当一个常量引用被绑定到另外一种类型上时


double dval =3.14; 
const int &ri = dval;

为了确保让常量整型引用 ri 绑定一个整数,编译器会把上述代码替换成如下形式


//由双精度浮点数生成一个临时的整型
const int temp = dval; 
//让ri绑定这个临时量
const int &ri = temp; 

因此 ri 绑定了一个临时量对象。当 ri 不是常量时,允许对 ri 赋值,则会改变ri所引用对象的值,这种情况显然不合理


临时量对象:当编译器需要一个空间来暂存表达式的求值结果时,临时创建一个未命名对象,临时量对象简称为临时量


2 对 const 的引用可能引用一个非const 的对象

常量引用对引用操作做了限定,对引用对象是不是常量未限定


其对象可能是非常量,也允许通过其它途径改变其值


int i =42;  
int &rl = i;       //引用ri绑定对象i
const int &r2 = i;  //不允许通过r2修改i的值 
rl =0;     // rl并非常量,i的值修改为0
r2 =0;     // 错误:r2是一个常量引用

2.4.2 指针和 const

与引用一样,指针可指向常量或非常量。和常量引用类似,指向常量的指针,不能改变所指对象的值。


要想存放常量对象的地址,只能使用指向常量的指针:


const double pi =3.14;  //pi是个常量,它的值不能改变
double *ptr = π    //错误:ptr是一个普通指针
const double *cptr = π //正确:cptr可指向常量
*cptr =42;     //错误:不能给*cptr賦值

除了两个例外,指针类型必须与其所指对象类型一致


第一种例外:指向常量的指针指向一个非常量对象


指向常量的指针仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其它途径改变


double dval =3.14; //dval是一个双精度浮点数,值可改变
cptr = &dval;    //正确:但不能通过cptr改变dval的值


1 const 指针

指针是对象而引用不是,类似于其它对象类型,可以把指针本身定为常量,称为常量指针。常量指针必须初始化,且初始化后,其值(存放在指针中地址)不能再改变


星号放在 const 关键字前,说明指针是一个常量。即不变的是指针值,而非指向值


int errNumb =0;
int *const curErr = errNumb;    // curErr将一直指向errNumb
const double pi =3.14159;
const double * const pip = π  // pip是一个指向常量对象的常量指针

判断声明含义有效办法是从右向左读,此例中离 curErr 最近符号是 const , 说明其一个常量对 象,对象类型由声明符其余部分决定


声明符剩余符号是 *,说明其是一个常量指针。最后,声明语句的基本数据类型部分说明常量指针指向一个 int对象


常量指针,不能改变存储的地址值


指向常量的指针,不能改变所指对象的值


指向常量的常量指针,对象值和存储的地址值都不能改变


2.4.2节练习

【练习2.27】
下面的哪些初始化是合法的?请说明原因。
(a) int i =-1, &r =0;         //不合法,引用的类型是对象,0是整型字面值
(b) int * const p2 = &i2;  //合法 常量指针,无法修改指针值
(c) const int i =-1, &r =0;  //合法,初始化常量引用,其能转换成引用类型即可
(d) const int * const p3 = &i2; //合法,指向const int 常量的常量指针 
(e) const int *pl = &i2;        //合法,指向const int常量的指针
(f) const int &const r2;  //不合法,引用必须初始化
(g) const int i2 = i, &r = i;   //合法
【练习2.28】
说明下面的这些定义是什么意思,挑出其中不合法的
(a) int i, * const cp;          //不合法 int整型,常量指针必须初始化
(b) int *pl, *const p2;   //不合法 指针指向int整型对象
(c) const int ic, &r = ic;    //合法 常量整型,常量引用
(d) const int *const p3;    //不合法 指向常量整型的常量指针必须初始化
(e) const int *p;    //合法 指向常量的指针
【习2.29】
假设己有上一个练习中定义变量,则下面哪些语句合法?请说明原因
(a) i = ic;      //合法 把常量整型赋值给整型
(b) pl = p3;      //合法 指向常量整型的常量指针赋值给指针
(c) pl = ⁣      //不合法 必须是指向常量指针
(d) p3 = ⁣     //合法 指向常量整型的常量指针
(e) p2 = pl;      //合法 用指针给常量指针赋值
(f) ic = *p3;     //合法


2.4.3 顶层 const

指针本身是不是常量,和指针所指对象是不是常量是两个独立的问题。用名词顶层 const 表示指针是个常量,而用名词底层 const 表示所指对象是常量


顶层 const 可以表示任意对象(任何数据类型都适用)是常量, 如算术类型、类、指针等。底层 const 则与指针、引用等复合类型的基本类型部分有关,指针类型比较特殊,既可以是顶层 const 也可以是底层 const


int i =0;
int * const pl = &i;       // 不能改变pl的值,这是一个顶层
const const int ci =42;    // 不能改变ci的值,这是一个顶层
const const int *p2 = &ci;  // 允许改变p2的值,这是一个底层const
const int *const p3 = p2;   // 既是顶层又是底层 const
const int &r = ci;          // 用于声明引用的const都是底层const

当执行对象的拷贝操作时,顶层和底层 const 区别


顶层const不受影响,因为拷贝操作并不会改变被拷贝对象值


底层 const :拷入拷出对象必须具有相同底层 const 资格,或两对象数据类型必须能转换


int *p = p3;    // 错误:p3包含底层const定义,而p没有
p2 = p3;                // 正确:p2和p3都是底层const
p2 = &i;                // 正确:int*能转换成 const int*
int &r = ci;            // 错误:普通的int&不能绑定到int常量上 
const int &r2 = i;      // 正确:const int&可以绑定到一个普通int上


总结:非常量可以转换成常量,反之不可


2.4.3节练习

【练习2.30】
对于下面的这些语句,请说明对象被声明成了顶层const还是底层const?
const int v2 =0;        // 底层
int vl = v2;         // 非 const
int *pl = &vl, &rl = vl;  // 非const
const int *p2 = &v2, *const p3 = &i, &r2 = v2;   // 底层,顶底层,顶层
【练习2.31】
假设己有上一个练习中所做的那些声明,则下面的哪些语句是合法的?
请说明顶层const和底层const在每个例子中有何体现
rl = v2;        // 不合法 赋值对象是底层const
pl = p2;         // 不合法 常量不可转化为非常量
p2 = pl;         // 合法 非常量可以转化为常量
pl = p3;         // 不合法 常量不可转化为非常量
p2 = p3;        // 合法


2.4.4 constexpr 和常量表达式

常量表达式是指值不会改变,且在编译过程就能得到计算结果的表达式,用常量表达式初始化的 const 对象也是常量表达式


一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定


const int max_files =20;          // max_files 是常量表达式
const int limit = max_files +1;   // limit 是常量表达式
int staff_size =27;               // staff_size 不是常量表达式
const int sz = get_size();         // sz 不是常量表达式
staff_size 的初始值是字面值常量,但它的数据类型是 int 
sz 是常量,但其值运行时才能获取,也不是常量表达式

constexpr 变量

复杂程序中,分辨一个初始值是不是常量表达式是困难的。C++11 新标准规定,允许将变量声明为 constexpr 类型,以便编译器验证变量值是否是常量表达式


声明为 constexpr 的变量一定是常量,且必须用常量表达式初始化


constexpr   int mf =20;            // 20 是常量表达式 
constexpr   int limit = mf +1;     // mf +1 是常量表达式
constexpr   int sz = size();    // 当 size 是一个 constexpr 函数时 
          //才是一条正确的声明语句

虽然不能使用普通函数作为 constexpr 变量初始值,但新标准允许定义一种 constexpr 函数。使编译时就可以计算结果,这样就能用函数去初始化 constexpr 变量了


建议:如果变量是常量表达式,那就把它声明成 constexpr 类型


字面值类型

常量表达式的值需要在编译时就得到计算,因此对声明 constexpr 时的类型有所限制


因为这些类型比较简单,值显而易见,就把它们称为 字面值类型 。算术类型、引用和指针都属于字面值类型,而自定义 类、I O 库、string 类型则不属于字面值类型


一个 constexpr 指针初始值必须是 nullptr 或 0 ,或是存储于某个固定地址中的对象


函数体内定义的变量一般不存放在固定地址中,而定义于函数体外的对象其地址不变


在 6.1.1 节还将提到,允许函数定义一类有效范围超出函数本身的变量,这类变量和定义在函数体之外的变量一样也有固定地址


指针和 constexpr

在 constexpr 声明中如果定义了一个指针,限定符 constexpr 仅对指针有效,与指针所指的对象无关


const int *p = nullptr;   // p 是一个指向整型常量的指针
constexpr int *q = nullptr;  // q 是一个指向整数的常量指针

2.4.4节练习

【练习2.32】
下面的代码是否合法?如果非法,请设法将其修改正确
int null =0, *p = null;
不合法 
int x =0; null是关键字
constexpr int *p = nullptr; 不能把整型赋值给指针

的变量一样也有固定地址


指针和 constexpr

在 constexpr 声明中如果定义了一个指针,限定符 constexpr 仅对指针有效,与指针所指的对象无关


const int *p = nullptr;   // p 是一个指向整型常量的指针
constexpr int *q = nullptr;  // q 是一个指向整数的常量指针

2.4.4节练习

【练习2.32】
下面的代码是否合法?如果非法,请设法将其修改正确
int null =0, *p = null;
不合法 
int x =0; null是关键字
constexpr int *p = nullptr; 不能把整型赋值给指针


相关文章
|
6月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
|
6月前
|
编译器 数据安全/隐私保护 C++
c++primer plus 6 读书笔记 第十三章 类继承
c++primer plus 6 读书笔记 第十三章 类继承
|
4月前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
29 1
|
4月前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
73 0
|
5月前
|
C++ Windows
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
在Windows上使用Visual Studio 2022进行FFmpeg和SDL2集成开发,首先安装FFmpeg至E:\msys64\usr\local\ffmpeg,然后新建C++控制台项目。在项目属性中,添加FFmpeg和SDL2的头文件及库文件目录。接着配置链接器的附加依赖项,包括多个FFmpeg及SDL2的lib文件。在代码中引入FFmpeg的`av_log`函数输出"Hello World",编译并运行,若看到"Hello World",即表示集成成功。详细步骤可参考《FFmpeg开发实战:从零基础到短视频上线》。
235 0
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
|
6月前
|
C++
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
|
6月前
|
SQL 人工智能 算法
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
|
6月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
|
6月前
|
存储 小程序 程序员
Essential C++ 第1章 C++编程基础 (笔记)
Essential C++ 第1章 C++编程基础 (笔记)
|
6月前
|
算法 C语言 C++
面向考试编程C++笔记
面向考试编程C++笔记