▌虚函数(Virtual functions):使用virtual关键字,总的来说是让函数符合对象的真实身份。
★★★为什么要使用虚析构函数?
答:在动态分配内存的时候,假如析构函数不为虚,则指向子类的基类指针在释放(delete)时,只释放子类中的基类部分,而附加部分不会释放,会引起内存泄漏。只要有一个类有派生类,析构函数最好为虚。使用时在“~”前加virtual。使用了虚析构之后,会符合对象的真实身份,子类对象会掉子类的析构函数,而子类的析构函数又会自动调用基类的析构函数,所以释放彻底,不会发生内存泄漏。
★当使用基类指针或引用指向子类的时候,当我们要求被调用函数(覆盖的函数)符合对象的真实身份时,我们要把这个函数定义为虚函数。此时我们不需要向下类型转化。
例:(不使用虚函数)
class Base
{
public:
void func();
};
class Derived:public Base
{
public:
void func(); //函数覆盖;
};
Base& b1=d;
Base* pd=&d;
Base b;
Derived d;
d.func(); //d是子类对象,所以调用子类func()方法;
b1.func(); //调用基类func()方法;
pd->func(); //调用基类func()方法;
(Derived*)pd->func(); //调用子类的func()方法(向下类型转化);
例:(使用虚函数)
class Base
{
public:
virtual void func();
};
class Derived:public Base
{
public:
virtual void func(); //函数覆盖;
};
Derived d;
Base& b1=d;
Base* pd=&d;
d.func(); //d是子类对象,所以调用子类func()方法;
b1.func(); //调用子类func()方法;
pd->func(); //调用子类func()方法;
//(Derived*)pd->func(); //调用子类的func()方法(向下类型转化);
例:(P77)
#include<iosteram>
using namespace std;
class Base
{
public:
void func(){
cout<<"Base class function.\n";
}
};
class Derived:public Base
{
public:
void func(){
cout<<"Derived class function.\n";
}
};
void foo(Base b){
b.func();
}
int main()
{
Derived d;
Base b;
Base* p=&d;
Base& br=d;
b=d;
b.func(); //基类方法;
d.func(); //子类方法;
p->func(); //基类方法,不符合对象的真实身份;
foo(d); //基类方法,不符合对象的真实身份;
br.func(); //基类方法,不符合对象的真实身份;
return 0;
}
★如果函数接受一个基类的引用则使用虚函数的时候符合函数的真实身份。
例:(P77)
#include<iosteram>
using namespace std;
class Base
{
public:
virtual void func(){
cout<<"Base class function.\n";
}
};
class Derived:public Base
{
public:
virtual void func(){
cout<<"Derived class function.\n";
}
};
virtual void foo(Base b){
b.func();
}
int main()
{
Derived d;
Base b;
Base* p=&d;
Base& br=d;
b=d;
b.func(); //基类方法;
d.func(); //子类方法;
p->func(); //子类方法,符合对象的真实身份;
foo(d); //基类方法,不符合对象的真实身份;
br.func(); //子类方法,符合对象的真实身份;
return 0;
}
上面的foo若改成virtual void foo(Base& b){b.func()},则调用子类的方法,符合对象的真实身份。
★如果在一个类里有虚函数,当我们delete子类指针时,则不会发生内存泄漏。
例:
class Base
{
~Base(){cout<<"b"<<endl;}
};
class Derived:public Base
{
~Derived(){cout<<"d"<<endl;}
};
Base* pd=new Derived();
delete pd; //基类指针指向子类对象,这里只能delete基类继承来的部分,附加部分会内存泄漏。
例:(P79)
#include<iostream>
#include<string>
using namespace std;
class Thing
{
public:
virtual void What_Am_I(){
cout<<"I am a Thing.\n";
}
~Thing(){
cout<<"Thing destructor."<<endl;
}
};
class Animal:public Thing
{
public:
virtual void What_Am_I(){
cout<<"I am an Animal.\n";
}
~Animal(){
cout<<"Animal destructor."<<endl;
}
};
int main()
{
Thing t;
Animal x;
Thing* array[2];
array[0]=&t;
array[1]=&x;
for(int i=0; i<2; ++i){
array[i]->What_Am_I();
}
return 0;
}
OUTPUT:
I am a Thing.
I am an Animal.
Animal destructor. //子类的析构函数;
Thing destructor. //调用子类的析构函数时自动调用基类的析构函数;
Thing destructor. //基类的析构函数;
例:(P80)
#include<iostream>
#include<string>
using namespace std;
class Thing
{
public:
virtual void What_Am_I(){
cout<<"I am a Thing.\n";
}
~Thing(){
cout<<"Thing destructor."<<endl;
}
};
class Animal:public Thing
{
public:
virtual void What_Am_I(){
cout<<"I am an Animal.\n";
}
~Animal(){
cout<<"Animal destructor."<<endl;
}
};
int main()
{
Thing* t=new Thing;
Animal* x=new Animal;
Thing* array[2];
array[0]=t;
array[1]=x;
for(int i=0; i<2; ++i){
array[i]->What_Am_I();
}
delete array[0]; //应该调用基类的析构;
delete array[1]; //应该调用子类的析构,但调用了基类的析构,不符合对象的真实身份;
return 0;
}
OUTPUT:
I am a Thing.
I am an Animal.
Thing destructor.
Thing destructor.
若上面的例子中在~Thing()和~Animal()前面加virtual,使其符合对象的真实身份,所以“delete array[1];”调用子类析构,同时自动调用基类的析构函数。
修改后的OUTPUT:
I am a Thing.
I am an Animal.
Thing destructor.
Animal destructor.
Thing destructor.
★★★一个类里面有虚函数或者他有子类,要使析构函数为虚。只要使基类的析构函数为虚,子类相同原型的函数和析构函数都为虚函数。
★动态绑定(dynamic binding):当一个基类里面有虚函数,当我们用基类指针指向子类对象或者基类引用引用一个子类对象时,则对象符合其真实身份。