📄 c++primer
字号:
Unix下,一次性编译和链接多个文件: $ CC -c main.cc Sales_item.cc # by default generates a.exe # some compilers generate a.out # puts the executable in main.exe $ CC -c main.cc Sales_item.cc -o main Unix下,分别编译多个文件: $ CC -c main.cc # 生成目标文件 main.o $ CC -c Sales_item.cc # 生成目标文件 Sales_item.o $ CC main.o Sales_item.o # 链接目标文件,默认生成 a.exe,有些编译器生成 a.out # puts the executable in main.exe $ CC main.o Sales_item.o -o main(2) 头文件用于声明而不是用于定义 C++ 中的任何变量都只能定义一次,定义会分配存储空间,而所有对该变量的使用都关联到同一存储空间。 定义只可以出现一次,而声明则可以出现多次。 同一个头文件可以被多个源文件包含。 头文件一般包含类的定义、extern 变量的声明和函数的声明;头文件不应该含有变量或函数的定义。 例如,头文件放有类似下面这些对象(变量)定义: extern int ival = 10; // 有初始化,这是定义,会分配内存 double fica_rate; // 没有 extern, 也是定义,会分配内存(3) 对于头文件不应该含有定义这一规则,有三个例外 下面三种定义允许放在头文件中: 类定义; 编译时就已知道的 const 对象; inline 函数(第7.6节介绍)。 在头文件中定义这些实体,是因为编译器需要它们的定义(不只是声明)来产生代码。 const 对象默认为定义它的文件的局部变量,把它们的定义放在头文件中是合法的。 在头文件中定义 const 变量,每个包含该头文件的源文件都有一份一样的 const 变量。 实际上,编译器在编译时就把常量表达式计算出结果,用结果直接替换 const 变量。 如果 const 变量不是用常量表达式初始化,那么它就不应该在头文件中定义; 而是和其他的变量一样,该 const 变量应该在一个源文件中定义并初始化; 然后应在头文件中为它添加 extern 声明,以使其能被多个文件共享。(4) 预编译头文件 如果头文件太大,编译时很费时间,为了减少处理头文件的编译时间,有些编译器支持预编译头文件。 欲进一步了解详细情况,请参考编译器的手册。2.9.2. 预处理器的简单介绍(1) 使用头文件 #include <iostream> // 包含标准头文件 #include "my_file.h" // 包含自定义的头文件 预处理器时会用头文件的内容替代 #include 。 预处理器处理程序的源代码,在编译器之前运行。(2) 预防多次包含同一头文件 #ifndef SALESITEM_H #define SALESITEM_H // 头文件内容放在里面 #endif第3章 标准库类型 基本数据类型外,C++ 还定义了一个内容丰富的抽象数据类型标准库。 其中最重要的标准库类型是 string 和 vector 它们分别定义了大小可变的字符串和集合。 另一种标准库类型 bitset,提供了一种抽象方法来操作位的集合。 本章将介绍标准库中的 vector、string 和 bitset 类型。3.1. 命名空间的 using 声明 #include <iostream> using std::cin; // using 声明将要用到标准库的cin,不必再加前缀:std::cin using std::cout; using std::endl; int main() { cout << "Enter two numbers:" << endl; int v1, v2; cin >> v1 >> v2; cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << endl; return 0; } 通常,头文件中应该只定义确实必要的东西。请养成这个好习惯。3.2. 标准库 string 类型 string 类型支持长度可变的字符串,C++ 标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作。 #include <string> // for string using std::string;3.2.1. string 对象的定义和初始化 string 标准库支持几个构造函数。当没有明确指定对象初始化式时,系统将使用默认构造函数。 字符串字面常量与标准库 string 类型不是同一种类型。 string s1; // 默认构造函数 s1 为空串 string s2(s1); // 将 s2 初始化为 s1 的一个副本 string s3("value"); // 将 s3 初始化为一个字符串字面值副本 string s4(n, 'c'); // 将 s4 初始化为字符 'c' 的 n 个副本3.2.2. string 对象的读写 // 读、写字符串 int main() { string s; // 空字符串 cin >> s; // 忽略开头空白字符(如空格,换行符,制表符),读到再次遇到空白字符为止 cout << s << endl; // 写两个字符串到屏幕 return 0; } // 可以把多个读操作或多个写操作放在一起 int main() { string s1, s2; cin >> s1 >> s2; // 读第1个输入给s1,第二个给s2 cout << s1 << s2 << endl; // 写两个字符串到屏幕 } // 可以把输入操作作为判断条件 int main() { string word; // 读到文件结束(end-of-file, EOF)为止,每行写一个单词到屏幕 while (cin >> word) cout << word << endl; return 0; } // 使用 getline 读取整行文本(不包括换行符) int main() { string line; // 一次读一行,读到文件结束(end-of-file, EOF)为止 while (getline(cin, line)) cout << line << endl; return 0; } getline 并不忽略行开头的换行符。只要 getline 遇到换行符,即便它是输入的第一个字符, getline 也将停止读入并返回。如果第一个字符就是换行符,则 string 参数将被置为空 string。3.2.3. string 对象的操作 s.empty() // 如果 s 为空串,则返回 true,否则返回 false s.size() // 返回 s 中字符的个数,返回的是 string::size_type 类型的值 s[n] // 返回 s 中位置为 n 的字符,位置从 0 开始计数 s1 + s2 // 把 s1 和s2 连接成一个新字符串,返回新生成的字符串 s1 = s2 // 把 s1 内容替换为 s2 的副本 v1 == v2 // 比较 v1 与 v2的内容,相等则返回 true,否则返回 false !=, <, <=, >, and >= // 保持这些操作符惯有的含义(1) string::size_type 类型 string::size_type 是 unsigned 型(unsigned int 或 unsigned long)。 为了避免溢出,保存一个 stirng 对象 size 的最安全的方法就是使用 string::size_type。(2) string 关系操作符 string big = "big", small = "small"; string s1 = big; // s1 是 big 的副本 if (big == small) // false // ... if (big <= s1) // true // ... string substr = "Hello"; string phrase = "Hello World"; string slang = "Hiya"; substr 小于 phrase,而 slang 则大于 substr 或 phrase (3) string 对象的赋值 // st1 is an empty string, st2 is a copy of the literal string st1, st2 = "The expense of spirit"; st1 = st2; // st1 中 st2 的一个副本 赋值操作确实做了一些工作: 先把 st1 占用的相关内存释放掉; 然后再分配给 st2 足够存放 st2 副本的内存空间; 最后把 st2 中的所有字符复制到新分配的内存空间。(4) 两个 string 对象相加 // 两个 string 相加 string s1("hello, "); string s2("world\n"); string s3 = s1 + s2; // s3 为 "hello, world\n" s1 += s2; // 相当于 s1 = s1 + s2 , 即把 s2 直接追加到 s1 的末尾 // 与字符串字面值相加,左右操作数必须至少有一个是 string 类型 string s1("hello"); string s2("world"); string s3 = s1 + ", " + s2 + "\n"; string s4 = s1 + ", "; string s6 = s1 + ", " + "world"; string s6 = "hello" + ", "; // 错误! string s7 = "hello" + ", " + s2; // 错误! string tmp = s1 + ", "; s5 = tmp + "world";(5) 从 string 对象读写字符 string str("some string"); for (string::size_type ix = 0; ix != str.size(); ++ix) cout << str[ix] << endl; for (string::size_type ix = 0; ix != str.size(); ++ix) str[ix] = '*'; string 对象的索引变量最好也用 string::size_type 类型。(6) 判断 string 对象是否为空 if (st.size() == 0) // ok: empty if (st.empty()) // ok: empty3.2.4. string 对象中字符的处理(1) cctype 头文件中的字符操作函数(C版是ctype.h) isalnum(c) 如果 c 是字母或数字,则为 true(非0) isalpha(c) 如果 c 是字母,则为 true isdigit(c) 如果 c 是数字,则为 true isxdigit(c) 如果 c 是十六进制数,则为 true ispunct(c) 如果 c 是标点符号,则 true isspace(c) 如果 c 是空白字符,则为 true,(空格、制表符、回车符、换行符、进纸符中的任意一种) iscntrl(c) 如果 c 是控制字符,则为 true isgraph(c) 如果 c 不是空格,但可打印,则为 true isprint(c) 如果 c 是可打印的字符,则为 true isupper(c) 如果 c 是大写字母,则 true islower(c) 如果 c 是小写字母,则为 true tolower(c) 如果 c 大写字母,返回其小写字母形式,否则直接返回 c toupper(c) 如果 c 是小写字母,则返回其大写字母形式,否则直接返回 c(2) 使用举例 // 计算字符串s中的标点符号个数 string s("Hello World!!!"); string::size_type punct_cnt = 0; for (string::size_type index = 0; index != s.size(); ++index) if (ispunct(s[index])) ++punct_cnt; cout << punct_cnt << " punctuation characters in " << s << endl; // 逐个字符转换成小写 for (string::size_type index = 0; index != s.size(); ++index) s[index] = tolower(s[index]); cout << s << endl;(3) 建议采用 C++ 版本的标准库头文件 C++ 标准库除了定义了一些选定于 C++ 的设施外,还包括 C 标准库。 C++ 中的 cctype 头文件其实就是利用了 C 标准库的 ctype.h 头文件。 C 标准库头文件命名形式为 name ;C++ 则命名为 cname ,少了后缀 .h,前面加 c 表示源自 C 标准库。 cctype 与 ctype.h 文件的内容是一样的,只是采用了更适合 C++ 程序的形式。 特别地,cname 头文件中定义的名字都定义在命名空间 std 内,而 .h 版本中的名字却不是这样。 通常,C++ 程序中应采用 cname 版本,而不采用 name.h 版本,确保标准库中的名字在命名空间 std 中保持一致。 使用 .h 版本会给程序员带来负担,因为必须记得哪些标准库名字是从 C 继承来的,而哪些是 C++ 所特有的。 3.3. 标准库 vector 类型 vector 是一种顺序容器。第9章更详细地介绍容器。 vector 也叫数组,但不同于内置数组,因它可以动态去添加元素。 vector 不是一种数据类型,是一个类模板(class template)。第16章介绍如何定义自己的类模板。 #include <vector> // 为了使用 vector 类模板 using std::vector; vector<int> ivec; // 元素类型为 int 的 vector vector<Sales_item> Sales_vec; // 元素类型为 Sales_items 的 vector3.3.1. vector 对象的定义和初始化 vector<T> v1; // vector 保存类型为 T 对象。默认构造函数 v1 为空。 vector<T> v2(v1); // v2 是 v1 的一个副本。 vector<T> v3(n, i); // v3 包含 n 个值为 i 的元素。 vector<T> v4(n); // v4 含有默认值的元素的 n 个副本。 例如: vector<int> ivec1; // 元素类型为 int 的 vector vector<int> ivec2(ivec1); // 把 ivec1 所有元素复制给 ivec2 vector<string> svec(ivec1); // 错误!svec 与 ivec1 的元素类型不一致 vector<int> ivec4(10, -1); // 10 个 int 元素,每个初始化为-1 vector<string> svec(10, "hi!"); // 10 个 string 元素, 每个初始化为"hi!" vector<string> fvec(10); // 10 个 int 元素,每个初始化为0 vector<string> svec(10); // 10 个 string 元素, 每个初始化为空字符串 vector 对象和其他标准库容(map、list等)器对象的重要属性就在于可以在运行时高效地添加元素。 vector 增长的效率高,在元素值已知的情况下,最好是动态地添加元素。 因此,高效使用方法是先初始化一个空 vector 对象,然后再动态地增加元素(后面将学习)。3.3.2. vector 对象的操作
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -