面试中的问题 -虚函数 多态
来源:互联网 发布:js获取div的value值 编辑:程序博客网 时间:2024/05/16 13:48
1、c++多态实现
C++中的虚函数的作用主要是实现多态的机制。关于多态,简而言之就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
例如:
#include <iostream>#include <stdlib.h>using namespace std;class Base{public:Base(){};~Base(){};virtual void Fun(){cout<<"Base Func"<<endl;}};class SubClass:public Base{public:SubClass(){};~SubClass(){}; void Fun(){cout<<"SubClass Fun"<<endl;}};int main(){SubClass sub;Base *pBase = ⊂//父类型的指针指向其子类的实例pBase->Fun();system("pause");return 0;}
这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
编译器会为每个有虚函数的类创建一个虚函数表(编译的时候),该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。
动态绑定与静态绑定
C++中虚析构函数的作用
用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?
class ClxBase{public: ClxBase() {}; virtual ~ClxBase() {}; virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };};class ClxDerived : public ClxBase{public: ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };};
pTest->DoSomething();
delete pTest;
的输出结果是:
Output from the destructor of class ClxDerived!
所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
为什么构造函数不能为虚函数:
因为如果构造函数为虚函数的话,它将在执行期间被构造,而执行期则需要对象已经建立,构造函数所完成的工作就是为了建立合适的对象,因此在没有构建好的对象上不可能执行多态(虚函数的目的就在于实现多态性)的工作。在继承体系中,构造的顺序就是从基类到派生类,其目的就在于确保对象能够成功地构建。构造函数同时承担着虚函数表的建立,如果它本身都是虚函数的话,如何确保vtbl的构建成功呢?
多重继承(无虚函数覆盖)
下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。
对于子类实例中的虚函数表,是下面这个样子:
我们可以看到:
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
多重继承(有虚函数覆盖)
下面我们再来看看,如果发生虚函数覆盖的情况。
下图中,我们在子类中覆盖了父类的f()函数。
下面是对于子类实例中的虚函数表的图:
我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以用任一个父类指针来指向子类,并调用子类的f()了。如:
Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()
b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()
尝试:通过父类型的指针(指向子类对象)访问子类自己的虚函数
我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到子类的虚表中有Derive自己的虚函数,但我们根本不可能使用基类的指针来调用子类的自有虚函数:
Base1 *b1 = new Derive();
b1->f1(); //编译出错
任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。
但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。
C++中虚拟继承的概念
为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题
http://blog.csdn.net/wangxingbao4227/article/details/6772579
- 面试中的问题 -虚函数 多态
- 关于“构造函数”中的几个小问题,也许面试会问到哦~
- 关于“构造函数”中的几个小问题,也许面试会问到哦~
- 面试笔试中的纯虚函数
- 面试中的手写函数
- sql 面试中的问题
- 面试中的找零问题
- 面试过程中的问题
- 面试中的开放性问题
- 面试中的数据库问题
- 面试中的问题
- 腾讯面试中的问题
- 面试中的C++问题
- 面试中的红黑树问题
- 面试中的多线程问题
- 面试中的一些问题
- 面试中的英语问题
- 多态,虚函数,纯虚函数,抽象类的相关问题(c++面试常见题目)
- 最大公约数gcd.cpp
- 范式
- HTML笔记之HTML规范及HTML操作思想
- jzoj1320 USACO-5.3.3Network of Schools
- 信息加密技术
- 面试中的问题 -虚函数 多态
- printf和scanf
- KVC/KVO
- c++构造函数、拷贝构造函数、析构函数、赋值运算符重载
- BottomBar(2.0新版)使用分析
- LeetCode ---- Single Number (Java/Lua 实现)
- Kullback-Leibler Divergence
- elasticsearch sql的使用
- stl 堆的简介 及poj2442