1.虚函数(重写的好兄弟):
虚函数是多态的得力助手之一。可以在子类继承到父类的成员函数时,在不改函数名的情况下,将他的功能重新定义,为己所用。
要注意的是,被static修饰的静态成员函数,被inline修饰的内联函数,构造函数不可以被声明为虚函数。当在基类的成员函数类型前加了virtual修饰后,继承他的子类中重写时也会自动加上。
class A
{
public:
virtual void func()
{
cout<<"A"<<endl;
}
}
2.虚析构函数
在继承中,将析构函数声明为虚析构函数是至关重要的,很多初学者往往忽视这一问题。平常写一些小程序一般是看不出有何问题的,但是这是非常危险的操作。先看一个例子:
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"contructor A!"<<endl;
}
virtual void func()
{
cout<<"A"<<endl;
}
~A()
{
cout<<"destructor A!"<<endl;
}
};
class B:public A
{
public:
B()
{
cout<<"contructor B!"<<endl;
}
void func()
{
cout<<"B"<<endl;
}
~B()
{
cout<<"destructor B!"<<endl;
}
};
int main()
{
A *a=new B;
a->func();
delete a;
return 0;
}
输出结果:
contructor A!
contructor B!
B
destructor A!
我们会发现系统只调用了A类的析构函数,而没有调用B类的,当我们在堆中自己分配了一块空间,我们就得在操作完成之后及时回收,不然会导致内存的泄露。上述例子就出现了这一问题。
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"contructor A!"<<endl;
}
virtual void func()
{
cout<<"A"<<endl;
}
virtual ~A()
{
cout<<"destructor A!"<<endl;
}
};
class B:public A
{
public:
B()
{
cout<<"contructor B!"<<endl;
}
void func()
{
cout<<"B"<<endl;
}
~B()
{
cout<<"destructor B!"<<endl;
}
};
int main()
{
A *a=new B;
a->func();
delete a;
return 0;
}
输出结果:
contructor A!
contructor B!
B
destructor B!
destructor A!
利用虚析构函数就能很好地解决这一问题。这是初学者很难注意到的问题,我要不是写这篇博客,可能也会经常疏忽。
3.纯虚函数:
纯虚函数,看上去比虚函数纯一点,也就是虚函数没有了函数体。其实,它的作用和虚函数是有很大不同的。
class A
{
public:
virtual void func()=0;
}
包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。基类中的纯虚函数在被子类中被定义。除非在子类中完全实现基类中所有的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
比如声明基类为形状,形状并不是具体的对象,但是不管什么形状都有其面积。我们可以声明一个求面积的纯虚函数。之后再声明一个子类为正方形,在正方形类中,如果不对求面积函数进行重写,将其定义,编译时将会报错。
#include<iostream>
using namespace std;
class Line
{
protected:
float len;//长
public:
Line(float len):len(len){}
virtual float area()=0;//表面积
virtual float volume()=0;//体积
virtual ~Line(){};
};
class Rec:public Line
{
protected:
float width;//宽
public:
Rec(float len,float width):Line(len),width(width){}
float area()
{
return len*width;
}
~Rec(){};
};
class Cuboid:public Rec
{
protected:
float hight;
public:
Cuboid(float len,float width,float hight):Rec(len,width),hight(hight){}
float area()
{
return 2*(len*width+len*hight+width*hight);
}
float volume()
{
return len*width*hight;
}
~Cuboid(){};
};
class Cube:public Cuboid
{
public:
Cube(float len):Cuboid(len,len,len){}
float area()
{
return 6*len*len;
}
float volume()
{
return len*len*len;
}
~Cube(){};
};
int main()
{
Line *p=new Cuboid(1,2,3);//长方体
cout<<"Cuboid area:"<<p->area()<<endl;
cout<<"Cuboid volume:"<<p->volume()<<endl;
delete p;
//p=new Rec(1,2);//未定义volume,为抽象类,编译会报错
p=new Cube(3);//正方体
cout<<"Cube area:"<<p->area()<<endl;
cout<<"Cube volume:"<<p->volume()<<endl;
delete p;
return 0;
}
纯虚函数的作用是为派生类提供一个一致的接口。所以他更像是基类给子类分配任务,也可以起到提示的作用。因为你如果没有完成任务,编译时就会报错。
就好像抽象类(基类)是个工程师,纯虚函数是建筑图纸。工程师绘制好图纸,然后图纸交给工人(也就是子类)。工程师只负责画图,他并不会垒砖建墙。而工人照着图纸建筑,合理分工,才能最终完成任务。
总结
很多人对多态不甚了解,很多概念容易弄混,希望看了文章可以给你带来帮助。还有虚析构函数的声明是最容易被人所遗忘的,像我们这些初学者一定要特别注意。