一、简介
虚函数就是类的成员函数前面加上virtual关键字的函数。虚函数的作用就是为了实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。使用了virtual程序将根据引用或指针指向的类型来选择方法,如果没有使用virtual程序将根据指针类型或引用类型来选择方法。
二、虚函数简单演示
在多态的公有继承中允许派生类重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。
#include<iostream>
using namespace std;
class A
{
private:
int a;
double b;
char c;
public:
void output1()
{
cout<< "A-output1" <<endl;
}
virtual void printf()
{
cout<< "A-printf" <<endl;
}
};
class B : public A
{
private:
int m;
char n;
public:
void output1()
{
cout<< "B-output1" <<endl;
}
void printf()
{
cout<< "B-printf" <<endl;
}
};
int main()
{
A a;
B b;
a = b;
a.output1();
a.printf();
A *pa = &b;
pa->output1();
pa->printf();
return 0;
}
在上面的程序中输出的结果为:
A-output1
A-printf
A-output1
B-printf
因此由输出结果可得知,对于基类中的虚成员函数可通过基类的指针来调用派生类中与基类的虚成员函数同名的函数(即C++中的多态机制)。它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
三、为何需要虚析构函数
析构函数应当是虚函数,除非类不用做基类。
#include<iostream>
using namespace std;
class A
{
private:
int a;
double b;
public:
void output1()
{
cout<< "A-output1" <<endl;
}
virtual void printf()
{
cout<< "A-printf" <<endl;
}
//~A()
virtual ~A()
{
cout<< "A-析构函数" <<endl;
}
};
class B : public A
{
private:
int m;
char n;
public:
void output1()
{
cout<< "B-output1" <<endl;
}
void printf()
{
cout<< "B-printf" <<endl;
}
~B()
{
cout<< "B-析构函数" <<endl;
}
};
int main()
{
A *pa = new B;
pa->output1();
delete pa;
return 0;
}
输出结果:
1)析构函数为虚:
A-output1
B-析构函数
A-析构函数
2)析构函数不为虚:
A-output1
A-析构函数
由以上代码的输出结果可知,当基类的析构函数不为虚函数时delete语句将只调用基类A的析构函数,这样只会释放B对象中A指向占用的内存,但不会释放派生类自己成员所占的内存,这样将会导致内存泄漏。但是如果基类A中的析构函数是虚函数,执行delete语句将先执行派生类B的析构函数,再执行基类A的析构函数,这样不会导致内存泄漏。
因此在通常情况下应给基类提供一个虚析构函数,计时它并不需要析构函数。
四、虚函数的注意事项
1.构造函数不能使虚函数。
创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数,然后派生类的构造函数将调用基类的一个构造函数,这种顺序不同于继承机制。因此派生类不继承基类的构造函数,所以将类的构造函数声明为虚函数没有什么意义。
2.友元函数不能是虚函数。
友元函数不是类成员,而只有成员才能是虚函数。