东莞网站推广营销网站设计厦门建设局网站
参考资料:
- 《C++ Primer》第5版
 - 《C++ Primer 习题集》第5版
 
2.4 const限定符(P53)
 
由于 const 对象在创建后不能修改,所以其必须初始化。
const 对象的常量特征仅在执行改变该变量的操作时才会发生作用。
const 对象默认仅在文件内有效。如果想在多个文件之间共享 const 对象,必须在变量定义前加 extern 关键字。
这部分涉及到多文件的内容,待补充!!!
2.4.1 const的引用(P54)
 
把引用绑定到 const 对象上,称为常量引用:
const int ci = 1;
const int &r = ci;
 
初始化和对const的引用
 
初始化常量引用时,可以使用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型:
const int &r1 = 1;double d = 3.14;
const int &r2 = d;
 
一般的引用要求类型严格一致
为什么常量引用具有上述特殊性呢?实际上,常量引用在绑定时引入了临时量:
const int temp = 1;
const int &r1 = temp;double d = 3.14;
const int temp = d;
const int &r2 = temp;
 
需要说明的是,当常量引用满足普通引用的条件时,不会引入临时量。不难看出,常量引用在上述情况中实质上是对临时量的引用。这也解释了普通的引用为什么不能通过临时量来实现跨类型引用,因为我们假如通过普通引用修改绑定对象,我们实质上将会修改临时量,而不会修改原来的变量,这显然与我们的初衷相违背,于是 C++ 将这种行为规定为非法行为。
2.4.2 指针和const(P56)
 
double pi = 3.14;// 常量指针
const double *p = π
// 指针常量
double *const p = *pi;
 
2.4.3 顶层const(P57)
 
以指针为例,顶层 const 表示指针本身是常量,底层 const 表示指针所指的对象的是常量。
对顶层 const 执行拷贝操作,没有任何限制:
int i = 1;
int *const p1 = &i;
int *p2 = p1;    // 正确
 
对底层 const 执行拷贝操作,则必须保证拷入对象同时具有底层 const 属性:
int i = 1;
const int *const p1 = &i;
int *p2 = p1;    // 错误,因为p2不具备底层const属性
const int *p3 = p1;    // 正确
 
2.4.4 constexpr和常量表达式(P58)
 
常量表达式指值不会改变且在编译过程中能得到结果的表达式。字面值属于常量表达式,用常量表达式初始化的 const 对象也是常量表达式。
一个对象或表达式是否是常量表达式是由其数据类型和初始值共同决定的:
const int max_file = 20;    // 是
const int limit = max_file+1;    // 是
int staff_size = 27;    // 不是
const int sz = get_size();    // 不是,因为编译时无法计算出结果
 
constexpr变量
在一个复杂程序中,我们很难分辨一个初始值是否为常量表达式。C++11 规定,可以通过将变量声明为 constexpr 来使编译器进行常量表达式检查。声明为 constexpr 的变量一定是常量,且必须用常量表达式初始化。
字面值类型
能用 constexpr 修饰的类型称为字面值类型,算术类型、引用和指针属于字面值类型,自定义类、IO 库、string 等类型不属于字面值类型。
用 constexpr 修饰的指针必须是 nullptr 或者具有固定地址的对象(定义在函数外的变量、静态变量等)
指针和constexpr
 
const int *p1 = nullptr;    // p1是常量指针
constexpr int *p2 = nullptr;    // p2是指针常量
 
2.5 处理类型(P60)
2.5.1 类型别名(P60)
类型别名和类型等价:
typedef double wages;
typedef wages base, *p;    // p是double*的同义词using wages = double;    // 新标准
 
指针、常量和类型别名
typedef char *pstring;
const pstring cstr1 = 0;    // cstr1是指针常量
const char *cstr2 = 0;    // cstr2是常量指针
 
个人感觉可以理解为:为复合类型起别名后,用这个别名声明变量时,该复合类型就成了这条声明语句的基本类型。
2.5.2 auto类型说明符(P61)
 
auto 类型说明符让编译器通过初始值推断变量的类型,因此 auto 定义的变量必须初始化。
由于一条声明语句只能有一个基本数据类型,故语句中所有变量的初始基本数据类型必须一样:
auto x1 = 0, *x2 = &x1;    // 正确
auto y1 = 0, y2 = 3.14;    // 错误
 
复合类型、常量和auto
 
当使用引用变量初始化auto变量时,编译器以被引用对象的类型作为 auto 的类型:
int i = 0, &r = i;
auto a = r;    // a为int型变量
 
auto 一般会忽略顶层 const ,但底层 const 会保留下来:
const int ci = 0, &cr = ci;
auto b = ci;    // b为int型变量,ci的顶层const属性被忽略
auto p = &ci;    // p为指向const int的指针
 
如果我们希望 auto 变量是顶层 const ,需要明确指出:
const auto x = ci;    // x为const int
 
还可以将引用的类型设为 auto ,此时的初始化规则同其他引用的初始化规则。
2.5.3 decltype类型指示符(P62)
 
decltype 的作用是返回操作数的数据类型,编译器分析表达式的类型但并不计算实际的值。与 auto 不同的是,如果 decltype 使用的表达式是一个变量,则 decltype 会返回该变量的类型(包括顶层 const 和引用):
int i = 0, &r = i;
decltype(r) c;    // 错误,c为引用类型,必须初始化
 
引用变量是被引用对象的代名词,只有在
decltype处是例外。
decltype和引用
 
如果 decltype 使用的是表达式而非变量,则 decltype 返回表达式结果对应的类型:
int i = 0;
decltype(i+1) c;    // 正确,c为int型变量
 
特别地,如果表达式的结果为左值,则
decltype返回引用类型。
如果表达式是解引用操作,则 decltype 将得到引用类型:
int i = 0, *p = &i;
decltype(*p) c;    // 错误,c为引用变量
 
这样的规则是符合逻辑的,因为解引用可以用来操作原变量,所以
decltype的结果应该是引用类型。
如果 decltype 所使用的表达式是变量名加上一对括号,结果将是引用:
decltype((i)) c;    // 错误,c为引用变量
 
这里涉及到“左值”的概念,待补充
2.6 自定义数据结构(P64)
C++ 允许用户以类的形式自定义数据结构。由于类体后面可以紧跟变量名(不推荐这么做),所以类定义后面必须加分号。
类数据成员
C++11 规定,可以为数据成员提供一个类内初始值,在创建对象是,类内初始值将用于初始化数据成员,没有初始值的成员被默认初始化。类内初始值可以放在花括号里、等号右边,不能放在圆括号里。
2.6.3 编写自己的头文件(P67)
类定义通常被定义在头文件中,且该头文件的名字应该和类一样。
头文件可能出现多次包含的情况。
预处理器概述
预处理器是用来确保头文件多次包含仍能安全工作的常用技术。预处理器在编译之前运行。预处理器看到 #include 标记时会用头文件的内容代替 #include 。
头文件保护符依赖于处理变量。预处理变量(NULL 就是一个预处理变量)有两种状态:已定义和未定义。define 指令把把一个名字定义为预处理变量;#ifdef 在变量已定义时为真,#ifndef 在变量未定义时为真,一旦检查结果为真,则执行到 #endif 为止。
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data {\* ... *\};
#endif
 
如果上述头文件已经被包含,则 ifndef 为假,编译器将忽略 ifndef 到 endif 之间的内容。
