1 虚函数
类中的成员函数原型前面加上virtual 表面这个函数是个虚函数。虚函数的目的是为了在继承它的派生类中又一次定义这个函数,以便于通过基类的指针或引用在执行时对派生类的函数进行调用。
2 派生类和虚函数
派生类普通情况下要重定义所继承的虚函数。有几个注意事项。
<1>虚函数的声明必须和基类中的函数声明原型全然一致。例外的是当基类返回基类型的指针或者引用的时候。派生类能够派生类类型的指针或者引用
<2>virtual 关键词加不加无所谓,可是一般要加上,能看的清楚这是个虚函数
<3>virtual的标签一旦在基类中的函数前面加上。就永远也去不掉
3 动态绑定的触发
C++的函数调用默认不使用动态绑定。使用动态绑定须要两个必须的条件
<1>通过基类类型的指针或者引用进行函数的调用
<2>函数是虚函数
通过这里也能够说明,当通过指针或者引用调用函数的时候,即便派生类重定义了基类的版本号。也不会调用派生类的版本号。由于它不会触发多态。
4 虚函数的覆盖机制
有时候通过指针或者引用不想触发多态。此时能够通过加限定符来显式的调用基类的函数
Derived d; Base *p = &d; p->Base::func();假设在派生类中的函数定义的时候想要调用基类的同名函数那么也要这样显式的调用,否则会引发无穷递归。
5 虚函数跟复制控制
<1>通常将析构函数设置为虚函数,这是由于析构函数在运行的时候仅仅会运行自身的部分。
比如通过基类的指针或者引用来调用析构函数的话,那么仅仅会调用基类的构造函数。派生类的构造函数将不会被调用。
<2>构造函数不能定义为虚函数,由于在构造函数是在对象全然构造之前执行的。在构造函数执行的时候,对象的动态类型还不完整。
<3>赋值操作符原理上能够设置为虚函数。可是这样做是没意义的。原因例如以下:
虚函数要求函数原型全然一致,这当中包含了函数參数类型的一致,假设我们把operator = 设置为虚函数。那么在基类中函数的參数是基类类型,在派生类虚函数的參数类型依旧是基类类型。可是赋值操作符是要求函数參数类型和类类型一致。这样就会产生很easy混淆的东西。
class Base{ public: virtual Base& operator =(Base& xx); } class Derived : public Base { public: virtual Derived& operator =(Base& xx); //virtual function Derived& operator =(Derived& xx); //real operator = }
6 构造函数和析构函数中的虚函数
在执行构造函数和析构函数的时候。此时的对象都不是一个完整的类。这个时候编译器将对象的对象视作在构造期间发生了变化,假设在当中调用虚函数的话。那么调用的将会是类自身类型
定义的版本号。
原因:基类在构造的时候,派生类部分的成员还没有初始化,假设此时调用的是派生类的虚函数,那么派生类的虚函数訪问类成员就会出问题。
同理基类的析构函数也是这样子。由于析构函
数直管自身成员的释放,派生类在调用析构函数的时候先调用自身的析构函数。然后再去调用基类的析构函数。所以基类的析构函数不可能调用派生类的虚函数,由于派生类的成员已经被释
放。实比例如以下:
//Base.h #pragma once #include <iostream> using namespace std; class Base { public: Base(void){ func();}; ~Base(void){ funp();}; virtual void func(){cout<<"this is in Base constructor"<<endl;} virtual void funp(){cout<<"this is in Base destructor"<<endl;}; };
//Derived.h #pragma once #include "base.h" class Derived : public Base { public: Derived(void){func();} ~Derived(void){func();} virtual void func(){cout<<"this is in Derived constructor"<<endl;} virtual void funp(){cout<<"this is in Derived destructor"<<endl;}; };
//main.cpp #include "Derived.h" void main() { Base X; Derived Y; }
7 纯虚函数
虚函数的形參后面跟上“=0”则表示纯虚函数。纯虚函数须要注意的有下面几点:
<1>纯虚函数没有函数体;
<2>最后面的“=0”并不表示函数返回值为0,它仅仅起形式上的作用。告诉编译系统“这是纯虚函数”;
<3>这是一个声明语句,最后应有分号。
<4>函数不能被调用
<5>不能创建有纯虚函数的类对象
<6>假设在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义。则该虚函数在派生类中仍然为纯虚函数。
含有一个或者多个纯虚函数的类被称为抽象类。抽象类不能创建对象。抽象类的意义在于提供功能基础接口。然后由派生类来实现,在此基础上实现多态。可是抽象类能够作为引用或者指针。
实例程序例如以下:
//Base.h #pragma once #include <iostream> using namespace std; class Base { public: Base(void){ func();}; ~Base(void){ funp();}; virtual void func(){cout<<"this is in Base constructor"<<endl;} virtual void funp(){cout<<"this is in Base destructor"<<endl;}; virtual void fuck()=0; };
//Derived.h #pragma once #include "base.h" class Derived : public Base { public: Derived(void){func();} ~Derived(void){func();} virtual void func(){cout<<"this is in Derived constructor"<<endl;} virtual void funp(){cout<<"this is in Derived destructor"<<endl;}; virtual void fuck(){cout<<"what a fuck day it is!"<<endl;} };
//main.cpp #include "Derived.h" void main() { Derived Y; Base& X = Y; }