有点意思的C/C++问题及解答:6-10

问题 6:非C++内建型别A 和B,在哪几种情况下B 能隐式转化为A? 

(1)class A { ...... };  class B : public A { ……} ;  // B 公有继承自A,可以是间接继承的

(2)class A { ...... }; class B {  operator A() { return A::A(); } ......};  // B 实现了到A 的类型转化
(3)class A { A( const B& ); } ; // A 实现了non-explicit 的参数为B(可以有其他带默认值的参数)构造函数 
提供隐式转换时,应避免出现二义性。比如上面的几种方式,如果即定义了(2)又定义了(3),就存在二义性。因为编译器可以用两种方式将B转换为A,一是用A的构造函数,另一种是用B的类型转换符。最佳实践:避免二义性最好的办法是避免编写互相提供隐式转换的成对的类。《C++ Primer》。

问题 7:下面这个程序有什么问题?

  1. #include <iostream>  
  2. using namespace std;  
  3. class A  
  4. {  
  5. private:  
  6.     int value;  
  7. public:  
  8.     A(int n) { value = n; }  
  9.     A(A other){ value = other.value; }  
  10.     void Print(){ cout << value << endl; }  
  11. };  
  12. int main()  
  13. {  
  14.     A a = 10;  
  15.     A b = a;  
  16.     b.Print();  
  17.     return 0;  
  18. }  

编译错误, A(A other){}  这个复制构造函数定义有误,该函数的形参只能是本类类型对象的引用,常用const修饰《C++ Primer》。应该改成这样 A(const A&other) { value = other.value; }。复制构造函数可以用于:
(1)根据另一个同类型的对象显示或隐式初始化一个对象
string s = "hello"; 
首先会调用接受一个C风格字符串形参的string构造函数,创建临时对象,然后调用复制构造函数,将s初始化为那个临时对象的副本。
(2)复制一个对象,将它作为实参传递给一个函数
void foo(string s);
(3)从函数返回复制一个对象
string foo();
(4)初始化容器中的元素。
vector<string> svec(5); 
首先调用string默认构造函数创建一个临时值,然后将这个临时值复制给svec的每一个元素,即调用string复制构造函数。
(5)根据元素初始化列表初始化数组元素
比如下面这个函数,初始化数组a 时调用的是复制构造函数。而初始化数组b时调用的是默认构造函数。而初始化数组c时,由于它有4个元素,而初始化列表只有2个,因此前两个元素的初始化调用复制构造函数,后两个元素的初始化调用默认构造函数。

  1. #include <iostream>  
  2. using namespace std;  
  3. class A  
  4. {  
  5. public:  
  6.     A()  { value=1;cout<<"A Default Constructor"<<endl;}  
  7.     A(int v) {value=v;cout<<"A Copy Constructor"<<endl;}  
  8.     int value;  
  9. };  
  10. int main()  
  11. {  
  12.     A a[2]={1,2};  
  13.         A b[2];  
  14.         A c[4]={1,2};  
  15.     return 0;  
  16. }  

问题8:构造函数、静态函数、内联函数可不可以是虚函数?

构造函数和静态函数不可以、内联函数可以
构造函数不可以为虚函数:虚函数的调用是通过一个虚指针来实现的,而这个虚指针是在构造过程中设定的。
静态函数不可为虚函数:静态函数的调用是不需要实例的,而虚函数需要通过从一个实例中获取虚指针,进而获取函数的地址,从而实现动态绑定。
内联函数可以:内联是在编译阶段用代码换调用,而虚函数是在运行期动态绑定。虽然可以,但是一般不会这样做,代价太大了。

问题9:析构函数必须是虚函数吗?

答案是并不是必须的。如果类被设计成能被继承,析构函数必须是虚函数。否则可以不为虚函数。析构函数写成虚函数主要是为了在实现多态时不造成内存泄露。比如下面这段程序,B的析构函数不会被调用,如果将A的析构函数写成虚函数,则B的析构函数可以被调用。

  1. #include <iostream>  
  2. using namespace std;  
  3. class A  
  4. {  
  5. public:  
  6.     A() {cout<<"A Constructor\n";}  
  7.     ~A() {cout<<"A Destructor\n";}  
  8. };  
  9. class B:public A  
  10. {  
  11. public:  
  12.     B() { cout<<"B Constructor\n";}  
  13.     ~B() { cout<<"B Destructor\n";}  
  14. };  
  15.    
  16. int main()  
  17. {  
  18.         A *pa=new B();  
  19.     delete pa;  
  20.     return 0;  
  21. }  
问题10:下面这个程序的输出是什么?
  1. #include <iostream>  
  2. using namespace std;  
  3. struct Point3D  
  4. {  
  5.     int x;  
  6.     int y;  
  7.     int z;  
  8. };  
  9.    
  10. int main()  
  11. {  
  12.     Point3D* pPoint = NULL;  
  13.     int offset = (int)(&(pPoint->z));  
  14.     cout<<offset<<endl;  
  15.     return 0;  
  16. }  
这个程序输出为8,&(pPoint->z)取成员变量z的地址,并不需要访问内存,所以只要将pPoint指向的地址加上变量z在类中偏移量,结果为8。

如果是&(pPoint->x)和&(pPoint->y),则结果为0和4。

如果pPoint不为空,例如 Point3D* pPoint = new Point3D;  三个成员变量的地址依次相差4。

猜你喜欢

转载自blog.csdn.net/nk_wang/article/details/44066855