虚函数的运行机制:如果基类中的非静态成员函数被定义称虚函数,且当派生类重写了基类的虚函数,,当通过指向基类对象的指针或引用 调用派生类对象中的虚函数时,就会调用到该指针(或引用)实际所指对象的成员函数
虚函数只能是类的成员函数,不属于任何类的普通成员函数不能定义称虚函数。
虚函数相关的代码书上有,网络上也有。
至于使用虚函数跟不使用虚函数的差别,以及虚函数带来的开销,可能例子少了一点。
举个例子:
C/C++ code
//2010.10.5
//一个提供功能的继承体系,和一个使用者
//非虚拟继承的实现
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////
class Base
{
public:
Base(int type) : type(type)
{}
void commonjob()
{
cout << "commonjob" << endl;
}
void personjob();
int type;//标识,用于区分不同的派生类
};
class Derived_a : public Base
{
public:
Derived_a():Base(1)//派生类类型1
{}
void personjob()
{
cout << "Derived_a::personjob" << endl;
}
};
class Derived_b : public Base
{
public:
Derived_b() : Base(2)//派生类类型2
{}
void personjob()
{
cout << "Derived_b::personjob" << endl;
}
};
//////////////////////////////////////////////////////////
class User
{
public:
void dojob(Base* bobj)
{
bobj->commonjob();
switch(bobj->type)
{
case 1:
((Derived_a*)bobj)->personjob();
break;
case 2:
((Derived_b*)bobj)->personjob();
break;
}
}
};
//////////////////////////////////////////////////////////
int main()
{
User user_obj;
Derived_a a_obj;
Derived_b b_obj;
cout << "--------test a--------" << endl;
user_obj.dojob(&a_obj);
cout << "--------test b--------" << endl;
user_obj.dojob(&b_obj);
return 0;
}
/*
--------test a--------
commonjob
Derived_a::personjob
--------test b--------
commonjob
Derived_b::personjob
*/
2、使用虚函数
C/C++ code
//2010.10.5
//一个提供功能的继承体系,和一个使用者
//虚拟继承的实现
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////
class Base
{
public:
void commonjob()
{
cout << "commonjob" << endl;
}
virtual void personjob() = 0;
};
class Derived_a : public Base
{
public:
void personjob()
{
cout << "Derived_a::personjob" << endl;
}
};
class Derived_b : public Base
{
public:
void personjob()
{
cout << "Derived_b::personjob" << endl;
}
};
//////////////////////////////////////////////////////////
class User
{
public:
void dojob(Base* bobj)
{
bobj->commonjob();
bobj->personjob();
}
};
//////////////////////////////////////////////////////////
int main()
{
User user_obj;
Derived_a a_obj;
Derived_b b_obj;
cout << "--------test a--------" << endl;
user_obj.dojob(&a_obj);
cout << "--------test b--------" << endl;
user_obj.dojob(&b_obj);
return 0;
}
/*
--------test a--------
commonjob
Derived_a::personjob
--------test b--------
commonjob
Derived_b::personjob
*/
分析:
通过那两个例子可以简单看出,当使用了虚函数的时候看起来比较简洁,便于阅读。
使用了虚函数,编译器会提供很多有用的支持。
性能分析:
使用了虚函数之后带来的开销:
1、继承体系中每一个类的虚函数表空间开销。
2、每一个对象的虚函数表指针空间开销。
3、每一次生成一个类都要去初始化虚函数表指针的时间开销。
4、每一次虚函数的调用都要通过虚函数表指针去访问虚函数表然后再调用函数,几经周折的时间开销。
5、使用虚函数之后不能使用内联函数。
比较:不使用虚函数表的性能开销
1、没有虚函数表空间开销。
2、有一个派生类类型标识的变量type的开销。
3、需要初始化type变量。
4、需要switch确定调用哪一个派生类的函数。
5、可以使用内联函数。
从简单的时间和空间开销上来说,虚函数比非虚函数多带来的坏处是:1、虚函数表的空间开销;2、无法使用内联函数。
但是从业务逻辑上来说,使用虚函数好处非常突出。当继承体系中增加了派生类时,不需要修改使用者的代码。
如果不使用虚函数,当继承体系中的派生类增加的时候,必须修改使用者的代码,这样的设计增加了代码的耦合度,模块化很差,不便于扩展、维护。
面向对象编程一个重要的目的是可扩展和可维护,当业务逻辑改变时对源程序的修改要尽可能的方便。
显然从程序设计的角度来说使用虚函数好很多。
参考《C 应用程序性能优化》第二章
阅读(4566) | 评论(0) | 转发(0) |