c++ 内存 继承

来源:互联网 发布:php大马源码 编辑:程序博客网 时间:2024/05/16 09:10

http://blog.csdn.net/randyjiawenjie/article/details/6829823


1.C++的虚函数是怎样实现的?

      C++的虚函数使用了一个虚函数表来存放了每一个虚函数的入口地址,这个虚函数表又使用一个虚函数指针来进行访问。通常,虚函数指针都放在对象模型的第一个位置存放,这样访问徐函数指针的速度最快,没有偏移量。通过虚函数指针,找到虚函数表,进而再做一个次偏移量计算,来得到真实虚函数的入口地址,从而访问了虚函数。这样看来,访问一个虚函数将使用两次间接计算,故要失去一些时间效率。另外,使用了虚函数,那么就要消耗一些空间,存放虚函数指针。但是,这都是值得的,为了实现多态。

2. 虚函数表是每个对象一个还是每个类一个呢?

     这个事搜狗的面试题目。其实讨论过,我之前看到这个问题,立马就想到了this指针。http://topic.csdn.net/u/20081031/16/6784c211-a475-4af5-a331-c3df887a2d0c.html比较详细的讨论。

    每个类只有若干个虚拟函数表,每个对象可以有若干个虚函数表指针。(这都是在多重继承的情况下出现的。单重继承的情况下,就是一个对象有一个虚函数表指针,每一个类有一个虚函数表)函数调用的时候会将this指针作为参数发过去,所以没有必要为每一个对象做一个虚拟函数表。
     “每个类有一个或多个虚函数表,单纯的继承下来的类有一个虚函数表,虚继承下来的类就有可能有多个虚函数表(只是有可能),对象没有虚函数表,但是有指向虚函数表的指针。对象之所一能调用“自己的函数”,是因为类的成员函数不知不觉的在编译时多了一个this指针参数来识别成员数据属于哪个对象。”

    关于这个问题,在《more effective c++》中的条款24有详细叙述,的确是每一个类的一个虚函数表。至于放的的位置,书中也有讲述。

3.函数指针和指针函数的区别?

     就想通过这个问题来判断我的C水平?C语言我看的数都有三本。

    函数指针,一个指针,指向了函数的调用地址。  void (*fun)(void)

  指针函数,就是一个函数的返回值是指针。 int *  fun(void)

  这道题目我真的很冤,指针函数,我真的没有听说过。

   数组指针和指针数组我知道什么区别

4. C++ 深拷贝和浅拷贝的区别?如果要delete一个浅拷贝,需要注意什么条件?

   现在有一个指针p1指向了一个内存空间m1;

   浅拷贝就是再用一个新的指针p2指向这片内存空间m1;

   深拷贝就是用一个新的指针p3指向m1的副本m2

   delete一个浅拷贝,首先要测试是不是有其它的指针还在指向这片空间。不然,直接就是野指针了。为什么野指针那么是绝对要禁止的?野指针现在指向了一片内存区间,这片内存区间以前是有意义的,现在被释放了,操作系统可能会讲这边区间放上其它程序数据。那么,野指针仍然指向了这片区间。如果此时使用野指针,改变了这片内存的数据,那么程序应该直接崩溃。而且,这样子的崩溃可能出现,可能等一会儿出现,不定。带来的bug很难查找。

5.  dynamic_cast的用法?

http://blog.csdn.net/wingfiring/article/details/633033

作为四个内部类型转换操作符之一的dynamic_cast和传统的C风格的强制类型转换有着巨大的差别。除了dynamic_cast以外的转换,其行为的都是在编译期就得以确定的,转换是否成功,并不依赖被转换的对象。而dynamic_cast则不然。在这里,不再讨论其他三种转换和C风格的转换。
首先,dynamic_cast依赖于RTTI信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查。
先看RTTI相关部分,通常,许多编译器都是通过vtable找到对象的RTTI信息的,这也就意味着,如果基类没有虚方法,也就无法判断一个基类指针变量所指对象的真实类型, 这时候,dynamic_cast只能用来做安全的转换,例如从派生类指针转换成基类指针.而这种转换其实并不需要dynamic_cast参与.
也就是说,dynamic_cast是根据RTTI记载的信息来判断类型转换是否合法的。

[cpp] view plaincopy
  1. struct B1{  
  2.     virtual ~B1(){}  
  3. };  
  4. struct B2{  
  5.     virtual ~B2(){}  
  6. };  
  7. struct D1 : B1, B2{};  
  8. int main()  
  9. {  
  10.     D1 d;  
  11.     B1* pb1 = &d;  
  12.     B2* pb2 = dynamic_cast<B2*>(pb1);//L1  
  13.     B2* pb22 = static_cast<B2*>(pb1);  //L2  
  14.     return 0;  
  15. }  

pb2将会为NULL指针,pb22将会编译出错。

[cpp] view plaincopy
  1. <span style="font-size:16px;">  
  2. </span>  
http://baike.baidu.com/view/1745213.htm

dynamic_cast < type-id > ( expression )  

该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;  

如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。  dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。  

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;  

在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

http://blog.csdn.net/wingfiring/article/details/633033

dynamic_cast 确实很好地解决了我们的问题,但也需要我们付出代价,那就是与 typeid 相比,dynamic_cast 不是一个常量时间的操作。为了确定是否能完成强制类型转换,dynamic_cast`必须在运行时进行一些转换细节操作。因此在使用 dynamic_cast 操作时,应该权衡对性能的影响


5. 请阅读以下的代码,试试看,你能发现错误或者不恰当地方吗?(提示:   不止一处)
http://topic.csdn.net/t/20061129/14/5194019.html

[cpp] view plaincopy
  1. class   Wheel{};  
  2. class   Color{};  
  3.   
  4. class   Car  
  5. {  
  6. private:  
  7.     Wheel*   wheels;  
  8. public:  
  9.     Car(int   wheel_count){wheels   =   new   Wheel[wheel_count];}  
  10.     ~Car(){delete   wheels;}  
  11. };  
  12.   
  13. class   Roadster   :   public   Car  
  14. {  
  15. public:  
  16.     Color   color;  
  17.   
  18.     Roadster(){};  
  19.     Roadster(const   Roadster&   rs)  
  20.     {  
  21.         this-> color   =   rs.color;  
  22.     }  
  23.     ~Roadster(){};  
  24.     Roadster&   operator=(const   Roadster&   rhs)  
  25.     {  
  26.         this-> color   =   rhs.color;  
  27.     }  
  28.     const   Roadster&   clone()  
  29.     {  
  30.         Roadster   the_new   =   *this;  
  31.         return   the_new;  
  32.     }  
  33. };  
  34.   
  35. int   main(int   argc,   char*   argv[]){  
  36.     Roadster*   yours   =   new   Roadster;  
  37.     Roadster   mine;     mine   =   yours-> clone();  
  38.     Car*   pCar   =   yours;  
  39.     delete   pCar;  
  40.     return   0;  
  41. };   

修改如下:

[cpp] view plaincopy
  1. class   Wheel{};  
  2. class   Color{};  
  3.   
  4. class   Car  
  5. {  
  6. private:  
  7.     Wheel*   wheels;  
  8.     //1.   没有保存wheels的下标  
  9.     //2.   最好用vector  
  10. public:  
  11.     Car(int   wheel_count){wheels   =   new   Wheel[wheel_count];}      
  12.     ~Car(){delete   wheels;}  
  13.     //3.   析构函数要虚拟  
  14.     //4.   delete   wheels;   ->   delete   []wheels;  
  15.     //5.   要提供拷贝构造函数  
  16.     //6.   要重载赋值操作符  
  17. };  
  18.   
  19. class   Roadster   :   public   Car  
  20. {  
  21. public:  
  22.     Color   color;  
  23.     //7.   成员变量要私有  
  24.   
  25.     Roadster(){};  
  26.     //8.   要调用父类的构造函数  
  27.     Roadster(const   Roadster&   rs)  
  28.     {  
  29.         this-> color   =   rs.color;  
  30.     }  
  31.     //9.   要调用父类的拷贝构造函数  
  32.     ~Roadster(){};  
  33.     //10.   析构函数要虚拟(原则:析构函数要么虚拟且公有,要么保护且非虚拟)  
  34.     Roadster&   operator=(const   Roadster&   rhs)  
  35.     {  
  36.         this-> color   =   rhs.color;  
  37.     }  
  38.     //11.   需要调用父类的赋值操作符  
  39.     //12.   没有返回值  
  40.     //13.   返回的类型最好是const   Roadster&,   而不是Roadster&  
  41.     const   Roadster&   clone()  
  42.     {  
  43.         Roadster   the_new   =   *this;  
  44.         return   the_new;  
  45.     }  
  46.     //14.   不能返回局部变量的引用  
  47.     //15.   按照这里的逻辑,   其实可以直接返回*this  
  48. };  
  49.   
  50. int   main(int   argc,   char*   argv[]){  
  51.     //16.   这里的{}风格与前面的不一致  
  52.     Roadster*   yours   =   new   Roadster;  
  53.     Roadster   mine;     mine   =   yours-> clone();  
  54.     //17.   最好是一行只有一条语句,   不要两条语句放在一行  
  55.     //18.   其实这里可以直接是Roadster   mine   =   yours-> clone();  
  56.     Car*   pCar   =   yours;  
  57.     delete   pCar;  
  58.     //我认为这里是没有问题的,作为Car类的使用者,他在这里用是没有问题的,错误的只是Car类的代码  
  59.     return   0;  
  60. };   

6.C++程序进入main函数之前,退出main函数之后会做些什么?

main函数执行之前,主要就是初始化系统相关资源:
1.设置栈指针
2.初始化static静态和global全局变量,即data段的内容
3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
4.运行全局构造器,C++中构造函数
5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数

main 函数之后会执行相反的工作。

原创粉丝点击