现在的位置: 首页 > 综合 > 正文

Thinking in C++ 第二章,第三章

2013年10月14日 ⁄ 综合 ⁄ 共 2859字 ⁄ 字号 评论关闭

1.    解释器和编译器:

      解释器将源代码转化成一些动作,并且立即执行。例如,BASIC 解释器一次翻译和执行一行,然后将这一行丢掉。Python 语言的解释器,先把整个程序

转化成某种中间语言,然后由执行速度更快的解释器来执行。

      C/C++编译时,首先要对源代码执行预处理,预处理的代码通常放在一个中间文件中,编译一般分两遍。第一遍进行语法分析,把源代码分解成小的单元并按树形结构组织,在第一遍和第二遍之间会使用全局优化器(global optimizer)来生成更短、更快的代码。第二遍由代码生成器遍历语法树,把结点转化成汇编或者机器代码。最后结果生成目标模块(.o 或者.obj文件)

     连接器(linker)把一组目标模块连接成一个可执行程序,操作系统可以装载和运行它。类型检查是在第一遍中完成的,而不是在程序运行阶段进行,因此C++ 是静态类型检查

2. 连接

      某些早期的连接器对目标文件和库文件只查找一次,这些连接器从左到右查找一遍给定的目标文件和库文件列表。因此目标文件和库文件的顺序就特别重要。当C 或者C++ 要对函数和变量进行外部引用时,根据引用情况,连接器会选择两种处理方法中的一种:如果还未遇到过这个函数或者变量的定义,连接器把它的标识符加到"未解析的引用"列表中。如果连接器遇到函数或者变量定义,那么就是已经解决的引用。

       因为连接器按指定的顺序查找文件,所以,用户使用与库函数同名的函数,把带有这种函数的文件插到库文件名列表之前,就能用他自己的函数取代库函数,因此被使用的是用户的函数而不是库函数。

      连接方式有两种:内部连接 和 外部连接。内部连接意味着只对正在编译的文件创建存储空间。用内部连接,别的文件可以使用相同的标识符或者全局变量,连接器不会发现冲突——也就是为每一个标识符创建单独的存储空间。在C 和 C++ 中,内部连接是由关键字static 指定的。外部连接意味着为所有被编译过的文件创建一片单独的存储空间。一旦创建存储空间,连接器必须解决所有对这片存储空间的引用。全局变量和函数有外部连接,通过extern 声明,可以从其他文件访问这些函数和变量。调用函数时,自动变量只是临时存在于堆栈中,连接器不知道自动变量。

3. C++ 的显式类型转换

static_cast 全部用于明确定义的变换,包括编译器允许我们所做的不用强制转换的”安全“变换和不太安全但清楚定义的变换。static_cast 包含的转换类型包括典型的非强制转换、窄化转换,使用void* 的强制转换、隐式类型转换和类层次的静态定位。 其实说白了,就是完成 C 语言定义的转换。

const_cast 从const转换为非const或者从volatile 转换为非volatile,可以使用const_cast。 这是const_cast 惟一允许的转换;如果进行别的转换就可能要使用单独的表达式或者可能会得到一个编译错误。

reinterpret_cast 这是最不安全的一种转换机制,最有可能出现问题。reinterpret_cast 把对象假想为模式,仿佛它是完全不同类型的对象。在使用reinterpret_cast做任何事情之前,实际上总是需要reinterpret_cast回到原来的类型

  1. #include<iostream>  
  2. using namespace std;  
  3. const int sz = 100;  
  4. struct X {  
  5.     int a[sz];  
  6. };  
  7. void print(X * x){  
  8.     for(int i = 0; i<sz; i++)  
  9.         cout << x->a[i] <<' ';  
  10.     cout << endl <<" -------------------------------" << endl;  
  11. }  
  12. int main(){  
  13.     X x;  
  14.     print (&x);  
  15.     int * xp = reinterpret_cast<int *>(&x);  
  16.     for(int * i = xp; i<xp+ sz; i++)  
  17.         * i = 0;  
  18.     //Can't use xp as an X* at this point  
  19.     //unless you cast it back;  
  20.   
  21.     //print(xp);  this is illegle  
  22.   
  23.     print(reinterpret_cast<X*>(xp));  
  24.     //In this example, you can also just use  
  25.     // the original identifier  
  26.     print(&x);  
  27. }  

xp 虽然指向了X* 类型的数据,但是不能当做这种类型来使用。这是最危险的一种转化方式,因为直接转化怎么对待内存中的0,1 值

dynamic_cast  显示类型转换,它是一种安全类型向下类型转换的操作。当使用dynamic_cast来试着向下类型转换一个特定的类型,仅当类型转换是正确的并且是成功的时候,返回值会是一个指向所需类型的指针,否则返回0 来表示这并不是正确的类型。

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. class Pet {  
  5. public:  
  6.     virtual ~Pet(){  
  7.     }  
  8. };  
  9. class Dog: public Pet{};  
  10. class Cat: public Pet{};  
  11. int main(){  
  12.     Pet *b = new Cat(); //upcast  
  13.     //Try to cast it to Dog*:  
  14.     Dog * d1 = dynamic_cast<Dog*>(b);  
  15.     //Try to cast it to Cat*:  
  16.     Cat * d2 = dynamic_cast<Cat*>(b);  
  17.     cout << "d1 = " << (long) d1 << endl;  
  18.     cout << "d2 = " <<(long)d2 << endl;  
  19. }  

当使用dynamic_cast 时候,必须对一个真正多态的层次进行操作——它含有虚函数——这是因为dynamic_cast使用了存储在VTABLE中的信息来判断实际的类型。上面的程序运行中可以看到不正确的向下类型转化返回了0 值。dynamic_cast 运行时需要一点额外的开销,虽然不多,但是执行大量的dynamic_cast,就会影响性能。有时,在向下类型转换时,我们可以知道正在处理的是何种类型,这时候使用dynamic_cast产生的额外开销就没药了,可以直接使用static_cast 来代替

抱歉!评论已关闭.