一、基础概念
我们知道,函数重载,运算符重载,模板属于编译时的多态形式,而虚函数则是运行时的多态形式。因此,在这里我们先来介绍一下什么时静态联编和动态联编。
静态联编
函数的重载,运算符的重载在编译器编译代码时,根据参数列表的匹配关系,就可以确定下来具体调用的函数,也就是在程序执行之前,就已经确定了函数调用关系。这属于静态联编。 参看如下代码:
#include<iostream>
using namespace std;
class Base
{
public:
void fun()
{
cout << "Base" << endl;
}
};
class Drive :public Base
{
public:
void fun()
{
cout << "Drive" << endl;
}
void fun(int a)
{
cout << "Drive(a)" << endl;
}
};
int main()
{
Base base;
Drive drive;
drive.fun();
drive.fun(5);
drive.Base::fun();
return 0;
}
动态联编
程序中函数调用与函数体代码之间的关联需要推迟到运行阶段才能确定,就是动态联编。
二、virtual在c++多态中的用法(虚函数)
在继承的类体系中,基类定义虚函数,派生类可自定义虚函数的不同实现版本,当程序用基类的指针或者引用调用该虚函数时将引发动态联编。系统会在运行阶段根据基类指针具体指向的对象调用该对象所属类的虚函数版本,从而实现运行时的多态。 参看如下代码:
#include<iostream>
using namespace std;
class Base
{
public:
void fun()
{
cout << "Base" << endl;
}
};
class Drive :public Base
{
public:
void fun()
{
cout << "Drive" << endl;
}
};
void f(Base *p)
{
p->fun();
}
int main()
{
Base base;
Drive drive;
f(&base);
f(&drive);
return 0;
}
#include<iostream>
using namespace std;
class Base
{
public:
virtual void fun()
{
cout << "Base" << endl;
}
};
class Drive :public Base
{
public:
virtual void fun()
{
cout << "Drive" << endl;
}
};
void f(Base *p)
{
p->fun();
}
int main()
{
Base base,*basePoint;
Drive drive,*drivePoint;
f(&base);
f(&drive);
basePoint=new Base;
f(basePoint);
delete basePoint;
drivePoint = new Drive;
f(drivePoint);
delete drivePoint;
return 0;
}
虚函数的注意事项:
- 虚函数必须是类的成员函数,不能是全局函数或者静态成员函数。
- 在派生类中重定义一个虚函数是,必须与基类的虚函数原型完全相同,否则其虚函数特性将丢失而成为普通的重载函数。
- 虚函数实现运行时多态的关键之处在于必须使用基类指针或引用调用虚函数。
- 一旦一个函数被声明为虚函数,其将保持虚函数特性,而不管其经历了多少层派生。
三、virtual在c++继承中的用法(虚继承)
在多继承中,如果多条继承路径上有一个公共的基类,则在这些路径的汇合点,便会产生来自不同路径的公共基类的数据成员的多份副本。如果想要只保留一份副本,就必须使用关键字virtual把这个公共基类定义为虚基类,这种继承被称为虚继承。虚继承使得最终的派生类对象中只保留公共基类(虚基类)的一份数据成员,避免了二义性问题。 eg:菱形继承就可以用虚基类(虚继承)来解决二义性问题。
四、virtual在析构函数中的用法(虚析构)
类中的析构函数可以声明为虚函数,即虚析构函数。 当用一个基类指针指向一个动态申请的派生类对象时,就需要用虚析构函数正确释放派生类对象。 注意: 构造函数不能是虚函数,原因是在建立一个派生类对象时必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数,不能在选择性地调用构造函数。
#include<iostream>
using namespace std;
class Base
{
public:
~Base()
{
cout << "Base::~Base is called" << endl;
}
};
class Drive :public Base
{
public:
~Drive()
{
cout << "Drive::~Drive called" << endl;
}
};
int main()
{
Base *basePoint=new Drive;
Drive *drivePoint=new Drive;
cout << "delete first object" << endl;
delete basePoint;
cout << "delete second object" << endl;
delete drivePoint;
return 0;
}
注意比较这两段代码的不同。
#include<iostream>
using namespace std;
class Base
{
public:
virtual ~Base()
{
cout << "Base::~Base is called" << endl;
}
};
class Drive :public Base
{
public:
virtual ~Drive()
{
cout << "Drive::~Drive called" << endl;
}
};
int main()
{
Base *basePoint=new Drive;
Drive *drivePoint=new Drive;
cout << "delete first object" << endl;
delete basePoint;
cout << "delete second object" << endl;
delete drivePoint;
return 0;
}
五、纯虚函数与抽象类
在上述二、virtual在c++多态中的用法时我们已经具体阐述了虚函数。接下来要叙述的是纯虚函数。 在许多情况下,在基类中并不能给出有意义的虚函数定义,这是就可以把它声明纯虚函数,即不需要具体定义函数的函数体,而把它的实现留给派生类来实现。 在基类定义虚函数的函数首部加上"=0",就是定义纯虚函数的语句。 含有纯虚函数的类我们称为抽象类,抽象类不能生成对象。 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出,如果派生类没有定义纯虚函数而只是继承基类的纯虚函数,那么这个派生类仍然还是一个抽象类,不能够生成对象。
|