简介
虚函数
在C++中虚函数是实现多态的一种机制,核心理念就是通过基类访问派生类定义的函数。
最常见的例子就是如下
class A
{
public:
virtual void foo()
{
cout << "A::foo() is called" << endl;
}
};
class B :public A
{
public:
virtual void foo()
{
cout << "B::foo() is called" << endl;
}
};
int main(void)
{
A *a = new B();
a->foo();
return 0;
}
这里函数foo通过关键字virtual声明为了虚函数,主函数中定义了一个父类A的指针a,指向一个子类B,此时再用a去调用foo函数,调用的则是子类B中的foo函数。如果没有virtual关键字,那么上述过程中的foo函数就会调用A类中的构造函数。
一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的这就叫做动态编联。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被称为“虚”函数。
纯虚函数和抽象类
纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”。
含有纯虚函数的类被称为抽象类,不能从抽象类中实例化对象出来。那么抽象类的作用是什么呢?
当我们以动物做一个基类,从这个基类中可以派生出老虎,狮子等动物,但是从动物基类中抽象出一个实例出来显然是不合理的。为了处理这类问题,便引入了纯虚函数,这样使派生类中必须重写基类中的纯虚函数,也就继承了基类的特性。
注意
1.当基类对象要对派生类对象进行操作的时候,析构函数在基类中设置为虚函数,为了防止析构时只析构基类而没有析构派生类。例如:
class A
{
public:
A()
{
ptra_ = new char[10];
}
~A()
{
delete[] ptra_;
}
private:
char * ptra_;
};
class B: public A
{
public:
B()
{
ptrb_ = new char[20];
}
~B()
{
delete[] ptrb_;
}
private:
char * ptrb_;
};
void foo()
{
A * a = new B;
delete a;
}
在这个例子中,程序也许不会象你想象的那样运行,在执行delete a的时候,实际上只有A::~A()被调用了,而B类的析构函数并没有被调用!这是否有点儿可怕?
如果将上面A::~A()改为virtual,就可以保证B::~B()也在delete a的时候被调用了。因此基类的析构函数都必须是virtual的。
2.构造函数任何情况下都不能设置为虚函数。因为虚函数的调用要在类的虚函数表中查找。而虚函数表是在构造函数中生成(准确来说是在初始化列表之前,所以构造函数中可以调用虚函数),如果构造函数为虚,就需要通过 虚函数表来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到虚函数表,所以构造函数不能是虚函数。