C++学习笔记――多态性和虚函数
2009-04-26 22:25
381 查看
多态性实现了接口和实现的分离。
捆绑:将函数体和函数调用相联系成为捆绑。分为早捆绑和晚捆绑,早捆绑在程序运行之前完成,晚捆绑反之。
C++中,虚函数实现晚捆绑。将基类的函数声明为virtual,对派生类的函数都将使用虚机制。实现运行时捆绑――晚捆绑。
实现:VTABLE&Vptr:
VTABLE:存放类中所有虚函数的地址
Vptr:指向VTABLE的指针,使函数通过基类指针调用虚函数时能匹配正确。
如:
Class Base
{
int param1;
public:
virtual void f(){ cout<<"base"<<endl;}
virtual int g(){ return 0 ;}
};
Class Derived:public Base
{
int param2;
public:
void f(){ cout << "deriverd"<< endl;}
int g(){ return 1; }
virtual string F(){return "derived";}
};
上面两个类的VTABLE分别为:
每当创建一个含有虚函数的类时,编译器就为这个类创建一个VTABLE,,并且在这个类中放置指向VTABLE首地址的指针Vptr。一旦VPTR被初始化为指向相应的VTABLE,对象就知道自己的类型。当通过基类地址调用一个虚函数时,如
Deriverd d;
Base * bptr = & d;
bptr-> g();
此时,虽然进行了向上类型转换,程序运行仍然调用派生类对象d的g()函数,返回1。程序通过基类指针bptr指向对象d的的首地址,由于VPTR存于对象中的相同位置,编译器能够取出对象d的VPTR,通过指针偏移获得函数g()的地址,进行调用,完成了晚捆绑。晚捆绑访问VTABLE,所以调用虚函数时有额外的开销,对函数调用的效率有所影响。
纯虚函数:将上面代码修改,如下:
Class Base
{
public:
virtual void f()=0;
virtual int g()=0;
};
基类的虚函数声明为“=0”的形式即声明为纯虚函数。纯虚函数可以没有定义,并且含有纯虚函数的类变成抽象类,不准生抽象类的对象。
派生类必须对基类所有的纯虚函数进行定义,否则仍然为抽象类,不能生成对象实例。
传值调用:对象切片问题
设存在函数 void function(Base b);函数接受基类对象b,如果声明一个派生类对象
Dervived d;
function(d);此时会发生向上类型转换,将派生类对象转换为Base类型的,对于Base类型的对象只有一个数据成员,而派生类的对象有两个,发生向上类型转换时,发生对象切片,派生类的私有数据成员param2丢失。如下表所示,分别为切片以前和切片以后的对象部分:
虚机制不作用于构造函数和析构函数,在构造函数和析构函数中调用的虚函数都是函数的本地版本。对于构造函数,因为虚函数所属的子类对象可能还没有生成,析构函数反之,这时调用发生错误。
可以有纯虚的析构函数,并且纯虚的析构函数必须有定义。虚析构函数的主要作用是防止实例化。
捆绑:将函数体和函数调用相联系成为捆绑。分为早捆绑和晚捆绑,早捆绑在程序运行之前完成,晚捆绑反之。
C++中,虚函数实现晚捆绑。将基类的函数声明为virtual,对派生类的函数都将使用虚机制。实现运行时捆绑――晚捆绑。
实现:VTABLE&Vptr:
VTABLE:存放类中所有虚函数的地址
Vptr:指向VTABLE的指针,使函数通过基类指针调用虚函数时能匹配正确。
如:
Class Base
{
int param1;
public:
virtual void f(){ cout<<"base"<<endl;}
virtual int g(){ return 0 ;}
};
Class Derived:public Base
{
int param2;
public:
void f(){ cout << "deriverd"<< endl;}
int g(){ return 1; }
virtual string F(){return "derived";}
};
上面两个类的VTABLE分别为:
&Base::f |
&Base::g |
&Derived::f |
&Dervived::g |
&Derived::F |
Deriverd d;
Base * bptr = & d;
bptr-> g();
此时,虽然进行了向上类型转换,程序运行仍然调用派生类对象d的g()函数,返回1。程序通过基类指针bptr指向对象d的的首地址,由于VPTR存于对象中的相同位置,编译器能够取出对象d的VPTR,通过指针偏移获得函数g()的地址,进行调用,完成了晚捆绑。晚捆绑访问VTABLE,所以调用虚函数时有额外的开销,对函数调用的效率有所影响。
纯虚函数:将上面代码修改,如下:
Class Base
{
public:
virtual void f()=0;
virtual int g()=0;
};
基类的虚函数声明为“=0”的形式即声明为纯虚函数。纯虚函数可以没有定义,并且含有纯虚函数的类变成抽象类,不准生抽象类的对象。
派生类必须对基类所有的纯虚函数进行定义,否则仍然为抽象类,不能生成对象实例。
传值调用:对象切片问题
设存在函数 void function(Base b);函数接受基类对象b,如果声明一个派生类对象
Dervived d;
function(d);此时会发生向上类型转换,将派生类对象转换为Base类型的,对于Base类型的对象只有一个数据成员,而派生类的对象有两个,发生向上类型转换时,发生对象切片,派生类的私有数据成员param2丢失。如下表所示,分别为切片以前和切片以后的对象部分:
Derived Vptr |
param1 |
param2 |
Derived Vptr |
param1 |
可以有纯虚的析构函数,并且纯虚的析构函数必须有定义。虚析构函数的主要作用是防止实例化。
相关文章推荐
- c++新手学习笔记之多态性和虚函数(1)
- 继承和多态和虚函数——C++学习笔记二
- C++学习 - 虚表,虚函数,虚函数表指针学习笔记
- C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类
- C++学习笔记(17)——虚函数与构造函数、析构函数
- C++程序设计学习之『多态性与虚函数』
- C++ 学习笔记(2) 虚函数的使用 virtual
- C++学习笔记13:类继承和派生、虚函数
- C++ FAQ学习笔记 20章 继承-虚函数
- C++虚函数和多态学习笔记
- C++学习笔记__类的派生和多态性
- 一步一步学习C++(类)之多态性与虚函数
- C++学习7-面向对象编程基础(多态性与虚函数、 IO文件流操作)
- C++虚函数和多态学习笔记
- C++学习笔记--虚函数的使用
- C++学习笔记之——c++多态性的类指针总结
- C++编程思想学习笔记---第15章 多态性和虚函数
- C++第十三周【任务1】开车学习虚函数、多态性和抽象类的应用
- C++学习笔记27,虚函数的工作原理
- C++学习之路—多态性与虚函数(一)利用虚函数实现动态多态性