C++面试题
写在前面的话:由于与C++语法相关的面试题,通常用很短的篇幅就能解释清楚,不适合写博客,因此本博客一直没有关注C++的语法题。近期发现篇幅短的C++题目刚好合适微博,于是开始在微博http://weibo.com/zhedahht和http://t.163.com/zhedahht上写C++的系列面试题。感兴趣的读者可以关注我的微博,或者直接围观面试题每日一题系列。同时,我也将不定期整理一些经典的C++面试题,发表到本博客上。
题目(一):我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数(写在函数的最后表示不能修改成员变量,不是指写在前面表示返回值为常量)。请问:能不能同时用static和const修饰类的成员函数?
分析:答案是不可以。C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时static的用法和static是冲突的。
我们也可以这样理解:两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。
题目(二):运行下面的代码,输出是什么?
1 class A 2 { 3 }; 4 5 class B 6 { 7 public: 8 B() {} 9 ~B() {} 10 }; 11 12 class C 13 { 14 public: 15 C() {} 16 virtual ~C() {} 17 }; 18 19 int _tmain(int argc, _TCHAR* argv[]) 20 { 21 printf("%d, %d, %d\n", sizeof(A), sizeof(B), sizeof(C)); 22 return 0; 23 }
分析:答案是1, 1, 4。class A是一个空类型,它的实例不包含任何信息,本来求sizeof应该是0。但当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。Visual Studio 2008中每个空类型的实例占用一个byte的空间。
class B在class A的基础上添加了构造函数和析构函数。由于构造函数和析构函数的调用与类型的实例无关(调用它们只需要知道函数地址即可),在它的实例中不需要增加任何信息。所以sizeof(B)和sizeof(A)一样,在Visual Studio 2008中都是1。
class C在class B的基础上把析构函数标注为虚拟函数。C++的编译器一旦发现一个类型中有虚拟函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32位的机器上,一个指针占4个字节的空间,因此sizeof(C)是4。
题目(三):运行下面中的代码,得到的结果是什么?
1 class A 2 { 3 private: 4 int m_value; 5 6 public: 7 A(int value) 8 { 9 m_value = value; 10 } 11 void Print1() 12 { 13 printf("hello world"); 14 } 15 void Print2() 16 { 17 printf("%d", m_value); 18 } 19 }; 20 21 int _tmain(int argc, _TCHAR* argv[]) 22 { 23 A* pA = NULL; 24 pA->Print1(); 25 pA->Print2(); 26 27 return 0; 28 }
分析:答案是Print1调用正常,打印出hello world,但运行至Print2时,程序崩溃。调用Print1时,并不需要pA的地址,因为Print1的函数地址是固定的。编译器会给Print1传入一个this指针,该指针为NULL,但在Print1中该this指针并没有用到。只要程序运行时没有访问不该访问的内存就不会出错,因此运行正常。在运行print2时,需要this指针才能得到m_value的值。由于此时this指针为NULL,因此程序崩溃了。
题目(四):运行下面中的代码,得到的结果是什么?
1 class A 2 { 3 private: 4 int m_value; 5 6 public: 7 A(int value) 8 { 9 m_value = value; 10 } 11 void Print1() 12 { 13 printf("hello world"); 14 } 15 virtual void Print2() 16 { 17 printf("hello world"); 18 } 19 }; 20 21 int _tmain(int argc, _TCHAR* argv[]) 22 { 23 A* pA = NULL; 24 pA->Print1(); 25 pA->Print2(); 26 27 return 0; 28 }
分析:答案是Print1调用正常,打印出hello world,但运行至Print2时,程序崩溃。Print1的调用情况和上面的题目一样,不在赘述。由于Print2是虚函数。C++调用虚函数的时候,要根据实例(即this指针指向的实例)中虚函数表指针得到虚函数表,再从虚函数表中找到函数的地址。由于这一步需要访问实例的地址(即this指针),而此时this指针为空指针,因此导致内存访问出错。
题目(五):静态成员函数能不能同时也是虚函数?
分析:答案是不能。调用静态成员函数不要实例。但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例。两者相互矛盾。
以上摘自何海涛博客