您的位置:首页 > 编程语言 > C语言/C++

【c++基础】虚函数的使用以及和成员函数的区别

2016-11-01 14:11 543 查看

前言

一直以来都知道虚函数的经典用法,但是除了本科时刚学c++的时候了解过,后来因为做不同的项目在不同的语言之间跳转(自觉都是浅尝辄止),这些基本的东西都忘记了,现在重拾并记录,权当巩固基础了。

经典用法

#include<iostream>
using namespace std;

//几何体类
class Geometry{
public:
virtual void draw(){
cout<<"Geometry Drawing"<<endl;
}
};

//矩形类
class Rect:public Geometry{
public:
virtual void draw(){
cout<<"Rect Drawing"<<endl;
}
};

//圆类
class Circle:public Geometry{
public:
virtual void draw(){
cout<<"Circle Drawing"<<endl;
}
};

void DrawSomething(Geometry *pG){
pG->draw();
}
int main(){
Geometry *geometrys[3];
//1.init
geometrys[0]=new Rect();
geometrys[1]=new Geometry();
geometrys[2]=new Circle();
//2.draw all
for(int i=0;i<3;i++){
DrawSomething(geometrys[i]);
}
return 0;
}


运行结果:

Rect Drawing

Geometry Drawing

Circle Drawing

用虚函数就可以实现
DrawSomething
函数这样接口简单的函数,而不用写
DrawRect
DrawCircle
这样的函数组。

另外这个例子不好的地方是,几何体实际上可以作为一个抽象的概念,应该限制它实例化、此时可以把其中的
draw
函数设置为纯虚函数
virtual void draw()=0;
.如此几何体类为一个抽象类,禁止实例化。

原理

首先来看一下c++对象模型图(来自《Inside The C++ Object Model》)



图中可以看到只有非静态成员变量和虚表指针是存在对象内的。静态成员函数、成员函数、静态变量是独立存储的(可以认为所有的类对象都共有一份)。虚表指针指向的虚表中存储了所有的虚函数。回到代码中,当执行这句话的时候
pG->draw();
时,编译器检测到是需要执行虚函数,就会通过该对象实例的vptr指针找到虚函数的地址,进而调用。所以即使子类的指针被转化为父类指针调用,当我们需要使用虚函数时,依然是调用子类的虚函数(转化的时候,vptr的没有变)。

现在再来看反面,假设我们去掉了上面代码所有的virtual 关键字。运行结果会是如何?结果是:

Geometry Drawing

Geometry Drawing

Geometry Drawing

从对象模型图中可以看出一些差别。该变量是什么类型,就会调用该类型的成员函数(我们的例中,都为Geometry *类型),没有一个vptr去做区分。有点类似于全局函数,如果对象或对象指针调用成员函数(非虚),实际上就是调用这个独立于类实例的函数指针。

总的来说不像虚函数和对象实例紧密相连(通过虚表指针),普通成员函数和类实例是分离的(见模型图)。但是由此产生的问题是,那成员函数如何去使用成员变量呢,实际上编译器隐式的给成员函数加上一个参数,使得调用时可以把this指针传进去,通过this指针就可以调用成员变量了。

补充

关于虚函数的实现原理,参考这篇博客 http://www.cnblogs.com/malecrab/p/5572730.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: