什么是虚函数?析构函数什么时候该声明为虚函数?什么是虚基类?
虚函数是Cpp用来实现多态的一种机制,但如何理解多态呢?人要工作,人派生出多个子类后,一个作家工作就是写文章,一个程序员工作却是写代码。工作的执行者不同,工作的内容也不同。 在类中成员函数前面加一个virtual,这个函数就变成了虚函数。如下代码:
class base{
public:
virtual void print(){
cout<<"this is base class"<<endl;
}
};
那么怎样让它起作用呢?当子类child继承base的时候,创建一个base类的指针,让它 指向子类对象,这时候用基类指针调用print(),此时会自动判断,然后调用子类中的函数, 如果基类不加virtual,基类指针就直接调用基类的print了。代码如下:
#include <iostream>
using namespace std;
class base{
public:
virtual void print(){
cout<<"this is base class"<<endl;
}
};
class child:public base{
public:
void print(){
cout<<"this is child class"<<endl;
}
};
int main()
{
child c;
base *ptr=&c;
ptr->print();
return 0;
}
在Java中,类中的成员函数默认都是虚函数。
如果想知道是怎么实现的呢,就要说起虚函数表了。 如果一个类中有虚函数,那么编译器会在类的开始位置设置一个虚函数指针,指向一个数组(每一个元素都是函数指针), 这个数组就是虚函数表,它存储着每个虚函数的地址。 当子类继承基类的时候,也会连这个虚函数表一块继承过来,然后把里面的函数指针改成子类中的虚函数地址。 由于使用虚函数会导致建立虚函数表,所以会使程序内存消耗变大,效率降低。 可以看这一篇文章探索C++虚函数在g++中的实现,讲的很详细。
明白了虚函数的特点,这个问题就不难了。 当基类指针指向子类对象的时候,在对象使用完毕需要释放时,肯定需要调用子类对象的析构函数呀,所以这种情况下析构函数也得是虚函数。 也可以看这段话:
基类指针可以指向派生类的对象(多态性),如果删除该指针delete []p;
就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用
基类的析构函数,这样整个派生类的对象完全被释放。
如果析构函数不被声明成虚函数,则编译器实施静态绑定,
在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,
这样就会造成派生类对象析构不完全。所以,将析构函数
声明为虚函数是十分必要的。
先看实现形式:
class Base{
public:
virtual void print(){
cout<<"this is Base class"<<endl;
}
};
class Child1:virtual public Base{
public:
void print(){
cout<<"this is Child1 class"<<endl;
}
};
当Child1虚继承Base时,就说Base是Child1的虚基类。 那这样做能解决什么问题呢? 有这样一种情况: 首先我们知道,当子类继承父类的时候,子类中会有父类的成员的一份拷贝。 当Child1,Child2继承Base,然后grandson又多继承自两个Child时,grandson中 就会有Child1,Child2,Base中的多份成员了。但是我们其实根本不需要这么多份 拷贝。先看代码:
#include <iostream>
using namespace std;
class Base{
public:
int a=1;
};
class Child1:public Base{
public:
};
class Child2:public Base{
public:
};
class Grandson:public Child1,public Child2{
public:
void print(){
cout<<a<<endl;//此行报错
}
};
int main()
{
Grandson grandson;
grandson.print();
return 0;
}
当我们使用虚继承的时候,就不会有多份拷贝了:
#include <iostream>
using namespace std;
class Base{
public:
int a=1;
};
class Child1:virtual public Base{
public:
};
class Child2:virtual public Base{
public:
};
class Grandson:public Child1,public Child2{
public:
void print(){
cout<<a<<endl;
}
};
int main()
{
Grandson grandson;
grandson.print();
return 0;
}
输出结果:
1
当使用多重继承的时候很容出现二义性问题,一般不推荐使用多重继承。 Java中就直接删除了多重继承的特性。
virtual <类型><函数名>(<参数表>)=0;
在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。 纯虚函数会让类成为抽象类,即不能被实例化。
如有错误,还请指正。 欢迎与我分享你的看法。 转载请注明出处:http://taowusheng.cn/