虚函数用法和注意事项总结 .

来源:互联网 发布:怎样查询淘宝店铺贷款 编辑:程序博客网 时间:2024/06/05 11:03
2014年1月微软MVP申请开始啦!    CSDN社区中秋晒福利活动正式开始啦!      专访钟声:Java程序员,上班那点事儿    独一无二的职位:开源社区经理    “说说家乡的互联网”主题有奖征文
虚函数用法和注意事项总结
分类: c/c++ 笔试面试2013-06-18 02:08 374人阅读 评论(0) 收藏 举报

     通常,编译器处理虚函数的方法是:给每个对象添加一个隐藏成员。隐藏函数中保存了一个指向函数地址数组的指针,这个地址数组是虚函数表,虚函数表中保存了类中声明的虚函数的地址。

     无论类中包含的虚函数是一个还是10个,都只在对象中添加一个指针,只是指向的地址表的大小不同而已。基类中会有一个指针指向一个虚函数表,派生类中也包含一个指针。

     1,若派生类,没有重新定义虚函数,则派生类的地址表将包含基类虚函数表的内容。

     2,若提供了虚函数的重新定义,则派生类的虚函数表将保存新的地址内容。

     3,若派生类添加了新的虚函数,则新的函数地址内容将添加到派生类的虚函数表中。

     虚函数表是用数组实现的,调用虚函数时,程序查看对象中指针指向的虚函数表地址,如果调用类声明中的第n个虚函数,则使用虚函数表----地址数组中的第n个元素。

     使用虚函数增加了内存和速度成本,

      1,每个对象都增大,曾大量为一个指针的大小

      2,对每个类,编译器都创建一个虚函数表(数组实现)

      3,每次虚函数调用,都要去表中查找地址

 

      虚函数要点

    1,在基类方法的声明中使用virtual关键字可使该方法在基类、子类以及子子类中都是虚函数

    2,如果使用指向对象的引用和指针来调用虚方法,程序将根据指向的对象类型来调用,若是调用非虚方法,则根据指针或者引用的类型来调用方法

    3,如果定义的类要用作基类,则要将在派生类中重新定义的方法声明为虚拟的。

 

     一些注意点:

     1,构造函数 

    构造函数不能是虚函数,因为派生类对象建立时调用的是自己的构造函数,并不是基类的构造函数,然后在派生类的构造函数中使用父类的构造函数,这不是继承,派生类并没有继承父类的构造函数,将构造函数声明为虚拟的是没有意义的。

     2,析构函数

    析构函数必须定义为virtual的派生类中继承了父类的内容,如果父类指针指向一个子类对象,当执行析构函数时,如果析构函数不是虚拟的,则调用父类的析构函数,子类新增的内容却无法释放。如果将析构函数定义为虚拟的,则先调用子类析构函数释放内容,再调用父类析构函数释放父类内容。

    3,友元函数

    友元函数不能是虚函数,因为它不是类成员,只有成员才能使虚函数

    4,没有重新定义

    如果派生类没有重新定义,则使用基本版本

    5,重新定义隐藏方法

    假设创建了以下代码


[cpp] viewplaincopyprint?
class base  
  
public:virtual void fun(int a);  
};  
class derive:public base  
 
public:virtual void fun();  
   
};  
class base {public:virtual void fun(int a);};class derive:public base{public:virtual void fun(); };

新定义的fun()为一个不接受任何参数的函数,重新定义不会生成函数个两个重载版本,而是隐藏了基类中接受一个参数的版本,简而言之,重新定义继承的方法并不是重载,如果在派生类中重新定义函数,而不是使用相同的函数特性来覆盖基类声明,则是隐藏同名的基类方法

    所以有两条经验

a如果要重新定义继承的方法,应确保与原来的原型完全相同但是如果返回类型是基类指针或引用,则可以修改为指向派生类的引用和指针,这种特性是返回类型协变,因为允许返回类型随类型的变化而变化


[cpp] viewplaincopyprint?
class base  
 
public:virtual base fun(int a);  
};  
   
class derive:public base  
 
public:virtual derive fun(int a);  
   
};  
class base{public:virtual base & fun(int a);}; class derive:public base{public:virtual derive & fun(int a); };

b如果基类声明被重载了,则应该在派生类中重新定义所有的版本。


[cpp] viewplaincopyprint?
class base  
 
public:virtual void fun(int a);  
          virtual void fun(double x);  
          virtual base fun();  
};  
   
class derive:public base  
 
public:virtual void fun(int a);  
          virtual void fun(double x);  
          virtual derive fun();  
   
};  
 
0 0