面向对象的程序设计-6-虚函数-part3

先注明下几个注意点:在派生类中重定义虚函数时必须有相同的函数原型,包括返回类型、函数名、参数个数、参数类型的顺序也必须是相同的。虚函数必须是类的成员函数,且不受public、protected或private的影响。同时,它也不能是全局的函数,也不能是静态的函数。析构函数可以是使虚函数,但是构造函数不能够是虚函数。为什么呢?1,从存储空间角度。虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。2,从使用角度。虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。当一个类打算被用作其他类的基类时,它的析构函数必须是虚的。以下面的例子进行讲诉:

#include <iostream>
using namespace std;
class X
{
private:
char *p;
public:
X(int size){p=new char[size];}//new出来的内存在堆里面
virtual ~X(){cout<<"~X"<<endl;delete []p;}//析构函数是虚函数
};
class Y:public X
{
private:
char *pp;
public:
Y(int sz1,int sz2):X(sz1)
{
pp=new char[sz2];
}
~Y()//自动成为虚函数
{
cout<<"~Y"<<endl;
delete []pp;
}

};
void main()
{
X *px=new Y(10,20);
delete px;//若不是虚函数,则Y没有被析构,则会造成内存的泄露
}
//析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。如果析构函数不是虚函数,而程序执行时又要通过基类的
//指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。

若是析构函数非虚,在执行delete px时,实际上只有X::~X()被调用了,而Y类的析构函数并没有被调用。将会造成内存的泄露……因此基类的析构函数都必须要有virtual的关键字。

如下面代码

#include <iostream>
using namespace std;
class A
{
public:
virtual void vf1(){cout<<"vf1 in base"<<endl;}
virtual void vf2(){cout<<"vf2 in base"<<endl;}
virtual void vf3(){cout<<"vf3 in base"<<endl;}
void f(){cout<<"f in base"<<endl;}
};
class B:public A
{
public:
void vf1(){cout<<"vf1 in derived"<<endl;};//虚函数
void vf2(int){cout<<"vf2 in derived"<<endl;};//这里的参数不同,不具有虚特性
void vf3(){cout<<"vf3 in derived"<<endl;};//当返回值类型不同,所以出现错误
void f(){cout<<"f in derived"<<endl;};//在基类中没有指明是虚函数,此时基类中的函数被覆盖
};
void main()
{
B b;
A *p=&b;//基类指针指向派生类对象
p->vf1();//调用B::vf1
p->vf2();//因为vf2已经不具备虚特性,所以直接访问的是基类A的vf2,即A::vf2()
b.vf2(1);//调用p->vf2(1)?
p->vf3();//当vf3不是虚函数时,访问基类
b.vf3();//
p->f();//同理调用基类A::f()
B *bp=&b;//派生类指针
bp->f();
bp->vf1();
bp->vf2(1);
bp->vf3();
}

具体的注释已经在代码写明了

你可能感兴趣的:(面向对象的程序设计-6-虚函数-part3)