【正确答案】
【答案解析】一个类中将所有的成员函数都尽可能地设置为虚函数总是有益的,但是设置虚函数需要注意以下5个方面的内容:
1)只有类的成员函数才能说明为虚函数。
2)静态成员函数不能为虚函数,因为调用静态成员函数不要实例,但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例,两者相互矛盾。
3)内联函数不能为虚函数。
4)构造函数不能为虚函数。
5)析构函数可以为虚函数,而且通常声明为虚函数。
构造函数不能是虚函数,因为构造函数是在对象完全构造之前运行的,换句话说,运行构造函数前,对象还没有生成,更谈不上动态类型了。构造函数是初始化虚表指针,而虚函数放到虚表里面,当要调用虚函数的时候首先要知道虚表指针,这就存在矛盾的地方了,所以构造函数不可能是虚函数。构造函数虽然不能是虚函数,但构造函数里可以调用虚函数。程序示例如下:
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
f();
}
virtual void f()
{
cout<<"base"<<endl;
}
};
class Derived:public Base
{
public:
Derived(){};
void f()
{
cout<<"Derived"<<endl;
}
};
int main()
{
Base*p=new Derived;
p->f();
return 0;
}
程序输出结果:
base
Derived
base
Derived
上例中,Base*p=new Derived,基类的指针生成了一个派生类对象,将会隐式调用base的构造函数,尽管对象是Derived,但构造基类部分时,还只是个Base,所以会调用基类的虚函数f()。
析构函数可以是虚函数,而且有的时候是必需的,基类指针指向派生类,用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。
析构函数执行时先调用派生类的析构函数,然后才调用基类的析构函数。如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全。程序示例如下:
#inelude<iostream>
using namespace std;
class CPerson
{
public:
virtual ~CPerson();
protected:
char * m_lpszName;
char * m_lpszSex;
};
class CStudent:public CPerson
{
public:
~CStudent();
protected:
int m_iNumjber;
};
CPerson::~CPerson()
{
cout<<"~CPerson!"<<endl;
}
CStudent::~CStudent()
{
cout<<"~CStudent!"<<endl;
}
int main()
{
CPerson * poCPerson=new CStudent;
if(NULL==poCPerson)
{
exit(0);
}
delete poCPerson;
cout<<"CStudent对象已经完成析构"<<endl;
CStudent oCSmdent;
return 0;
}
程序输出结果:
~CStudent!
~CPerson!
CStudent对象已经完成析构
~CStudent!
~CPerson!