《Thinking in C++》学习笔记(一)【第二章】
第二章 对象的创建与使用
2.1语言的翻译过程
翻译器分为两类:解释器(interpreter)和编译器(compiler)。
2.1.1解释器
解释器将源代码转化成一些动作(它可由许多机器指令组成)并立即执行这些动作。
解释器必须驻留内存以执行程序。
2.1.2编译器
编译器直接把源代码转化成汇编语言或机器指令。
分段编译(separate compilation):某些语言(如C语言)可以分别编译各段程序,最后使用连接器(linker)把各段程序连接成一个完整的可执行程序。
人们把测试通过并能正常运行的程序块收集起来加入库(library)中,供其它程序员使用。
源代码层的调试器(source-level debugger):通过跟踪程序经过源代码的进展来显示程序的执行情况。
2.1.3编译过程
C/C++编译时首先要对源代码执行预处理。预处理器(preprocessor)是一个简单的程序,它用程序员定义好的模式代替源代码中的模式。预处理过的代码通常放在一个中间文件中。
编译分两遍进行。首先对预处理过的代码进行语法分析。编译器把源代码分解成小的单元并把它们按树形结构组织起来——语法分析树。
使用全局优化器(global optimizer)来生成更短、更快的代码。
编译的第二遍,由代码生成器(code generator)遍历语法分析树,把树的每个结点转化成汇编语言或机器代码。最后生成目标模块(.o或.obj文件)
使用窥孔优化器(peephole optimizer)从相邻代码中查找冗余汇编语句。
连接器(linker) 把一组目标模块连接成一个可执行程序,操作系统可以装载和运行它。当某一目标模块中的函数要引用另一目标模块中的函数或变量时,由连接器来处理这些引用,连机器还要添加一个特殊的目标模块来完成程序启动任务。
连接器能搜索称为”库“的特殊文件来处理所有的引用。库将一组目标模块包含在一个文件中。
2.2.3.1静态类型检查(static type checking)动态类型检查(dynamic type checking)
类型检查(type checking)是编译器在第一遍中完成的。
2.2分段编译工具
程序分割的最基本的方法是创建命名子程序。C/C++里子程序成为函数(function),函数是一段代码段,可以将这些函数放在不同的文件中,并能分别编译。
声明(declaration):“告知编译器”外部函数和数据的名称及它们的模样。“声明”是向编译器介绍名字——标示符。它告诉编译器这个函数或这个变量在某处可找到,它的模样像什么。而“定义”是说:“在这里建立变量或在这里建立函数”,它为名字分配存储空间。
单一定义规则(one-definition rule):C/C++可以在不同的地方吗相同的变量和函数,但只能有一个定义。
2.2.1.1函数声明的语法
在函数声明时,可以给出参数命,编译器会忽略这些参数命名。
extern关键字:它表示变量是在文件以外定义的,或在文件后面部分才定义。
2.2.1.5包含头文件
大部分库包含众多的函数和变量,为了减少工作量,确保一致性,当对这些函数变量做外部声明时,C/C++使用“头文件”(header file)。头文件是一个包含某个库的外部声明函数和变量的文件。
包含头文件,要使用#include预处理命令,他告诉预处理器打开指定的头文件并在#include语句所在的地方插入头文件。#include有两种方式:
(a)尖括号指定头文件:预处理器以特定的方式寻找文件,一般是环境中或编译器命令行指定的某种寻找路径。
(b)双引号指定头文件:预处理器以“由实现定义的方式”来寻找文件,从当前目录开始寻找,如果文件没有找到就与按尖括号同样的方式重新寻找。
2.2.1.6标准C++ include语句格式
标准使用的格式允许文件名长度可以大于8个字符,去除了扩展名。
使用.h头文件是老的、非模板化的版本,而没有.h的文件是新的模板化版本。
2.2.2连接
连接器把由编译器生成的目标模块(.o或.obj文件)连接成为操作系统可以加载和执行的程序。
2.2.3使用库文件
连接器建立目标模块列表,把标示符加到“未解析的引用”列表中。如果遇到过函数或变量的定义,那么这就是已解决的引用,否则去查找库,把库中包含所需定义的模块加入连接。
因为连接器按指定的顺序查找文件,用户使用与库函数同名的函数,被使用的是用户的函数而不是库函数,这可能是一个bug,C++名字空间禁止这样做。
2.2.3.2秘密的附加模块
当创建一个可执行程序时,连接器会秘密连接某些模块。其中之一是启动模块,它包含了对程序的初始化例程,初始化例程建立堆栈,并初始化程序中的某些变量。
连接器总是从标准库中查找程序中调用的经过编译的“标准”函数。标准库总可以被找到,只要程序中包含所需头文件就可以使用库中的任何模块,不要告诉连接器去找标准库,如果使用附加的库,必须把该库文件名添加到由连接器处理的列表文件中。
2.3.2名字空间
namespace关键字:当程序达到一定规模后,函数名和标示符不够用,预防这种冲突的机制。
using关键字:如果只包含头文件,编译器无法找到任何有关函数和对象的声明,要告诉编译器所使用的名字空间。
对已经存在的代码提供向后兼容:#include<iostream.h> 相当于 #include<iostream> using namespace std;
2.3.3程序的基本结构
C的注释行以 “ /* ”开始,以“ */ ”结束,其中可以包含换行符。
C++注释符“ // ”,注释从“ // ”开始,到换行符结束。
2.4.1字符数组的拼接
如果两个加引号的字符数组邻接,并且它们之间没有标点,编译器就会 把这些字符数组连接成单个字符数组。
2.5字符串简介
可以用“=”给string对象赋值
连接string对象用“+”操作符
将string加到一个string之后用“+=”操作符
2.6文件的读写
头文件<fstream>,且自动包含<iostream>
创建一个ifstream对象或ofstream对象
getline()函数,第一个参数是ifstream对象,第二个参数是string对象。getline()将丢弃掉换行符。
//Copy one file to another,a line at a time #include<string> #include<fstream> using namespace std; int main() { ifstream in("Scopy.cpp"); ofstream out("Scopy2.cpp"); string s; while(getline(in,s)) out<< s <<"\n"; }
2.7 vector简介
在vector后追加一个新元素,可以使用成员函数push_back()
元素可以由下标(indexing)选定
//Creating a vector that holds inergers #include<iostream> #include<vector> using namespce std; int main() { vector<int> v; for(int i=0;i<v.size();i++) v.push_back(i); for(int i=0;i<v.size();i++) cout<<v[i]<<","; cout<<endl; for(int i=0;i<v.size();i++) v[i]=v[i]*10; //Assignment for(int i=0;i<v.size();i++) cout<<v[i]<<","; cout<<endl; }