非 const 变量默认为 extern。要使 const 变量能够在其他的文件中访问,必须地指定它为 extern。
extern int ival = 10; // initializer, so it's a definition
double fica_rate; // no extern, so it's a definition
虽然 ival 声明为 extern,但是它有初始化式,代表这条语句是一个定义。类似地,fica_rate 的声明虽然没有初始化式,
但也是一个定义,因为没有关键字 extern。同一个程序中有两个以上文件含有上述任一个定义都会导致多重定义链接错误。
引用就是对象的另一个名字。
引用必须用与该引用同类型的对象初始化:
int ival = 1024;
int &refVal = ival; // ok: refVal refers to ival
int &refVal2; // error: a reference must be initialized
int &refVal3 = 10; // error: initializer must be an object
typedef 可以用来定义类型的同义词:
typedef double wages; // wages is a synonym for double
typedef int exam_score; // exam_score is a synonym for int
typedef wages salary; // indirect synonym for double
typedef 通常被用于以下三种目的:
1.为了隐藏特定类型的实现,强调使用类型的目的。2.简化复杂的类型定义,使其更易理解。
3.允许一种类型用于多个目的,同时使得每次使用该类型的目的明确。
枚举
每个 enum 都定义了一种新的类型。和其他类型一样,可以定义和初始化 Points 类型的对象,也可以以不同的方式使用这些对象。
枚举类型的对象的初始化或赋值,只能通过其枚举成员或同一枚举类型的其他对象来进行:
Points pt3d = point3d; // ok: point3d is a Points enumerator
Points pt2w = 3; // error: pt2w initialized with int
pt2w = polygon; // error: polygon is not a Points enumerator
pt2w = pt3d; // ok: both are objects of Points enum type
注意把 3 赋给 Points 对象是非法的,即使 3 与一个 Points 枚举成员相关联。
class 和 struct区别
用 class 和 struct 关键字定义类的唯一差别在于默认访问级别:默认情况下,struct 的成员为 public,而 class 的成员为 private
避免多重包含(头文件)
在编写头文件之前,我们需要引入一些额外的预处理器设施。预处理器允许我们自定义变量。
预处理器变量 的名字在程序中必须是唯一的。任何与预处理器变量相匹配的名字的使用都关联到该预处理器变量。
为了避免名字冲突,预处理器变量经常用全大写字母表示。
预处理器变量有两种状态:已定义或未定义。定义预处理器变量和检测其状态所用的预处理器指示不同。
#define 指示接受一个名字并定义该名字为预处理器变量。#ifndef 指示检测指定的预处理器变量是否未定义。
如果预处理器变量未定义,那么跟在其后的所有指示都被处理,直到出现 #endif。
可以使用这些设施来预防多次包含同一头文件:
#ifndef SALESITEM_H
#define SALESITEM_H
// Definition of Sales_itemclass and related functions goes here
#endif
条件指示
#ifndef SALESITEM_H
测试 SALESITEM_H 预处理器变量是否未定义。如果 SALESITEM_H 未定义,那么 #ifndef 测试成功,
跟在 #ifndef 后面的所有行都被执行,直到发现 #endif。相反,如果 SALESITEM_H 已定义,
那么 #ifndef 指示测试为假,该指示和 #endif 指示间的代码都被忽略。
为了保证头文件在给定的源文件中只处理过一次,我们首先检测 #ifndef。第一次处理头文件时,测试会成功,
因为 SALESITEM_H 还未定义。下一条语句定义了 SALESITEM_H。那样的话,如果我们编译的文件恰好又一次包含了该头文件。
#ifndef 指示会发现 SALESITEM_H 已经定义,并且忽略该头文件的剩余部分。
#include 指示接受以下两种形式:
#include <standard_header> #include "my_file.h"
string类的用法
警告:标准库 string 类型和字符串字面值 因为历史原因以及为了与 C 语言兼容,字符串字面值与标准库 string 类型不是同一种类型。这一点很容易引起混乱,编程时一定要注意区分字符串字面值和 string 数据类型的使用,这很重要。
Table 3.2. string Operations
s.empty() | Returns true if s is empty; otherwise returns false 如果 s 为空串,则返回 true,否则返回 false。 |
s.size() | Returns number of characters in s 返回 s 中字符的个数 |
s[n] | Returns the character at position n in s; positions start at 0. 返回 s 中位置为 n 的字符,位置从 0 开始计数 |
s1 + s2 | Returns a string equal to the concatenation of s1 and s2 把 s1 和s2 连接成一个新字符串,返回新生成的字符串 |
s1 = s2 | Replaces characters in s1 by a copy of s2 把 s1 内容替换为 s2 的副本 |
v1 == v2 | Returns true if v1 and v2 are equal; false otherwise 比较 v1 与 v2的内容,相等则返回 true,否则返回 false |
!=, <, <=, >, and >= | Have their normal meanings 保持这些操作符惯有的含义 |
任何存储 string 的 size 操作结果的变量必须为 string::size_type 类型。特别重要的是,还要把 size 的返回值赋给一个 int 变量。 |
Table 3.3. cctype Functions
isalnum(c) |
True if c is a letter or a digit. 如果 c 是字母或数字,则为 True。 |
isalpha(c) |
true if c is a letter. 如果 c 是字母,则为 true。 |
iscntrl(c) |
true if c is a control character. 如果 c 是控制字符,则为 true |
isdigit(c) |
true if c is a digit. 如果 c 是数字,则为 true。 |
isgraph(c) |
true if c is not a space but is printable. 如果 c 不是空格,但可打印,则为 true。 |
islower(c) |
true if c is a lowercase letter. 如果 c 是小写字母,则为 true。 |
isprint(c) |
True if c is a printable character. 如果 c 是可打印的字符,则为 true。 |
ispunct(c) |
True if c is a punctuation character. 如果 c 是标点符号,则 true。 |
isspace(c) |
true if c is whitespace. 如果 c 是空白字符,则为 true。 |
isupper(c) |
True if c is an uppercase letter. 如果 c 是大写字母,则 true。 |
isxdigit(c) |
true if c is a hexadecimal digit. 如果是 c 十六进制数,则为 true。 |
tolower(c) |
If c is an uppercase letter, returns its lowercase equivalent; otherwise returnsc unchanged. 如果 c 大写字母,返回其小写字母形式,否则直接返回 c。 |
toupper(c) |
If c is a lowercase letter, returns its uppercase equivalent; otherwise returnsc unchanged. 如果 c 是小写字母,则返回其大写字母形式,否则直接返回 c。 |
// an iterator that cannot write elements
vector<int>::const_iterator
// an iterator whose value cannot change
const vector<int>::iterator
迭代器是个所谓的智能指针
3.4.1. 迭代器的算术操作
iter + niter - n
可以对迭代器对象加上或减去一个整形值。这样做将产生一个新的迭代器,其位置在 iter 所指元素之前(加)或之后(减) n 个元素的位置。加或减之后的结果必须指向 iter 所指 vector 中的某个元素,或者是 vector 末端的后一个元素。加上或减去的值的类型应该是 vector 的 size_type 或 difference_type 类型(参考下面的解释)。
iter1 - iter2
该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值,这里的 difference_type 是 signed 类型,因为减法运算可能产生负数的结果。该类型可以保证足够大以存储任何两个迭代器对象间的距离。iter1 与 iter2 两者必须都指向同一 vector 中的元素,或者指向 vector 末端之后的下一个元素。
实例:
vector<int>::iterator mid = (vi.begin() + vi.end()) / 2; × ←没有定义两个迭代器相加
string* ps1, ps2; // ps1 is a pointer to string, ps2 is a string
defines ps1 as a pointer, but ps2 is a plain string. If we want to define two pointers in a single definition, we must repeat the * on each identifier:
实际上只把 ps1 定义为指针,而 ps2 并非指针,只是一个普通的 string 对象而已。如果需要在一个声明语句中定义两个指针,必须在每个变量标识符前再加符号 * 声明:
string* ps1, *ps2; // both ps1 and ps2 are pointers to string
初始化动态分配的数组
string *psa = new string[10]; // array of 10 empty strings int *pia = new int[10]; // array of 10 uninitialized ints这两个 new 表达式都分配了含有 10 个对象的数组。其中第一个数组是 string 类型,分配了保存对象的内存空间后,将调用 string 类型的默认构造函数依次初始化数组中的每个元素。第二个数组则具有内置类型的元素,分配了存储 10 个 int 对象的内存空间,但这些元素没有初始化。
也可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化(第 3.3.1 节):
int *pia2 = new int[10] (); // array of 10 uninitialized ints
圆括号要求编译器对数组做值初始化,在本例中即把数组元素都设置为0。
引用形参
为了使 swap 函数以期望的方式工作,交换实参的值,需要将形参定义为引用类型:
// ok: swap acts on references to its arguments
void swap(int &v1, int &v2)
{
int tmp = v2;
v2 = v1;
v1 = tmp;
}
与所有引用一样,引用形参直接关联到其所绑定的圣贤,而并非这些对象的副本。定义引用时,必须用与该引用绑定的对象初始化该引用。引用形参完全以相同的方式工作。每次调用函数,引用形参被创建并与相应实参关联。此时,当调用 swap
swap(i, j);
形参 v1 只是对象 i 的另一个名字,而 v2 则是对象 j 的另一个名字。对 v1 的任何修改实际上也是对 i 的修改。同样地,v2 上的任何修改实际上也是对 j 的修改。重新编译使用 swap 的这个修订版本的 main 函数后,可以看到输出结果是正确的:
Before swap(): i: 10 j: 20
After swap(): i: 20 j: 10
static 局部对象
一个变量如果位于函数的作用域内,但生命期跨越了这个函数的多次调用,这种变量往往很有用。则应该将这样的对象定义为 static(静态的)。static 局部对象确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化。这种对象一旦被创建,在程序结束前都不会撤销。当定义静态局部对象的函数结束时,静态局部对象不会撤销。在该函数被多次调用的过程中,静态局部对象会持续存在并保持它的值。
inline 函数
1.编译时展开
2.声明和实现最好都放在头文件里,方便调用的函数展开。
7.9. 指向函数的指针
函数指针是指指向函数而非指向对象的指针。像其他指针一样,函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定,而与函数名无关:// pf points to function returning bool that takes two const string references
bool (*pf)(const string &, const string &);
这个语句将 pf 声明为指向函数的指针,它所指向的函数带有两个 const string& 类型的形参和 bool 类型的返回值。
*pf 两侧的圆括号是必需的:
// declares a function named pf that returns a bool*
bool *pf(const string &, const string &);