记录了学习虚函数与纯虚函数中有一些疑问,以及平常可能不注意的地方。
Q0:虚函数是怎么实现的?
0:简单的说,是通过虚函数表实现的。如果一个类中含有虚函数,则系统会为这个类分配一个指针成员指向一张虚函数表(vtbl),表中每一项指向一个虚函数的地址,实现上就是一个函数指针的数组。
Q1:基类函数加上virtual关键字,派生类不加,那么派生类的同名函数是虚函数吗?
1.C++继承中,如果基类定义了一个虚函数,那么派生类中的同名函数不用加virtual(也可以加)关键字,也可以使该函数为虚函数。因为派生类继承了基类的属性,所以派生类中同名函数被视为与基类有相同属性的函数。
如果基类为纯虚函数,那么派生类中也不用声明virtual,但是必须实现纯虚函数。
纯虚函数基类中定义:virtual fun() = 0;派生类可以有不同的实现,即可以称在基类中提供了该函数的接口。
Q2:那为什么需要这个接口呢,比如我直接可以在子类中分别写同名函数,然后一一实现。
2.看到的一个很形象例子:比如基类的接口类似与电脑的USB接口,不同的设备都可以插入访问数据,U盘、鼠标、键盘;如果分别实现的话,那么就需要在电脑上加三个不同类型的接口,而纯虚函数的作用,则只需要这一个接口来对不同的需求进行操作,非常方便。
Q3:构造函数可以是虚函数吗?析构函数呢?
3:构造函数不能是虚函数,因为要构造一个对象,就必须知道构造了什么;析构函数一般定义为虚函数,称为虚析构函数(指向派生类对象的基类指针被delete时,会调用派生类析构函数、基类的析构函数,否则只会调用基类的析构函数),可以定义为纯虚函数。
(可以参考 http://www.cnblogs.com/chio/archive/2007/09/10/888260.html)
讲了平常不会注意的地方:
纯虚函数也可以定义函数体;
6) 析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
7) 非纯的虚函数必须有定义体,不然是一个错误。
虚函数的作用:(例子)
1 class Base 2 { 3 public: 4 virtual void Draw(){}; 5 }; 6 7 class DerivedA:public Base 8 { 9 public: 10 DerivedA(){}; 11 void Draw() 12 { 13 printf("A draw "); 14 } 15 }; 16 17 class DerivedB:public Base 18 { 19 public: 20 DerivedB(){}; 21 void Draw() 22 { 23 printf("B draw "); 24 } 25 };
如果要DerivedA或者DerivedB调用Draw(),可以分别new它们的对象,然后调用Draw(),但是如果还有其他的派生类,则需要定义并new很多个对象;
那么,虚函数的作用:
Base* base = new DerivedA; 或者 Base* base = new DerivedB;
base->Draw(); //按照new的对象,对应调用派生类的Draw()函数,只需要定义一个base指针即可。
纯虚函数的作用:(例子)
class Vehicle { public: virtual void Print()=0; //纯虚函数的定义 }; class Car:public Vehicle { public: virtual void Print(){cout<<"Car"<<endl;}; }; class Bike:public Vehicle { public: virtual void Print(){cout<<"Bike"<<endl;}; }; void main() { Car c; Bike b; b.Print(); //输出Bike c.Print(); //输出Car }
这里Vehicle为抽象类,也叫纯虚类;定义了print()=0;派生类Car和Bike实现了print()函数,按照对象调用各自的print()函数。
Q4:如何通过指向派生类对象的指针或引用调用基类对象的虚函数?(参考https://blog.csdn.net/chijianxingfeng/article/details/8870387)
例一:
#include <iostream> using namespace std; class A { public: virtual void show() { cout<<"in A::show() "; } }; class B:public A { public: void show() { cout<<"in B::show() "; } }; int main() { A a; //通过派生类对象的引用pb 实现了调用基类中虚函数show(),, //如果把 A中show() 前面的virtual去掉, 则调用的就是B 中的show() B &pb = static_cast<B&>(a); pb.show(); //调用的是基类 A的 show(); return 0; }
输出:
例二:
#include <iostream> using namespace std; class A { public: virtual void show() { cout<<"in A::show() "; } void callfunc() { show(); } }; class B:public A { public: void show() { cout<<"in B::show() "; } }; int main() { B b; b.callfunc(); //调用的是A::callfunc(),,但在A::callfunc()调用的是B::show() //这就是一个虚调用 A a; a.callfunc(); //这里调用的是A::show() return 0; }
输出: