多态的定义和构成的条件
多态是指两个类之前存在继承的关系,而在派生类发生了重写基类中函数的过程,而这个函数必须是返回值,参数个数与数据类型还有函数名都是相同的,并且这个函数还是个虚函数,这样才能发生多态的现象,这里可以写一段代码来实现一下,如果该函数不是虚函数只能是静态多态也就是在编译时就已经确定好了,这样的多态就没有意义了。
重写函数是虚函数
#include <iostream>
#include <string>
using namespace std;
class father{
public:
virtual void func(){
cout<<"这是父类的函数调用"<<endl;
}
};
class son:public father{
public:
virtual void func(){
cout<<"这是子类的函数调用"<<endl;
}
};
void test(){
father *f=new son;
f->func();
cout<<"son类的大小"<<sizeof(son)<<endl;
cout<<"father类的大小"<<sizeof(father)<<endl;
delete f;
}
int main(){
test();
return 0;
}
这里可以看到是多态成功了,创建了一个指针来接收开辟的子类对象,并且可以看到这里类的大小是8,因为我这是在64位操作系统上的,所以是一个指针的大小,所以该类内部是存在一个虚函数指针,它指向虚函数表,当子类重写了虚函数时,如果发生多态就相当于把父类的内容覆盖了。
重写函数不是虚函数时
这里代码就不展示了,只需要把上边的父类中的virtual去掉就行,这里看到虽然父类对象指向了子类开辟的对象,但是调用的还是父类中的内容,并且类中大小只是1也就是默认的大小,所以是没有虚函数指针的,发生的多态也只是静态多态,在编译阶段就已经生成了,所以调用的还是父类中的函数。
抽象类
什么是抽象类呢,就是不能实例化对象的类,可以这样理解,它是抽象的,所以实例化是没有意义的,所以编译器索性让你不能实例化,怎么判断这个类是不是抽象类呢,当一个类中包含了纯虚函数时,这个类就是一个抽象类,抽象类的子类必须要重写父类中的纯虚函数,否则该子类或者说派生类也是抽象类,该类也不能实例化了,那么这样的多态就没有意义了。
纯虚函数语法:virtual 返回值类型 函数名(参数)=0
eg:virtual void func()=0;
#include <iostream>
#include <string>
using namespace std;
class father{
public:
virtual void func()=0;
};
class son:public father{
public:
void func(){
cout<<"这是子类的函数调用"<<endl;
}
};
void test(){
father *f=new son;
f->func();
cout<<"son类的大小"<<sizeof(son)<<endl;
cout<<"father类的大小"<<sizeof(father)<<endl;
delete f;
}
int main(){
test();
return 0;
}
这里可以看到其实和加了虚函数的是类似的,只是在父类中的函数是不需要写函数体的。
虚析构和纯虚析构
我们既然有虚函数,那么也理应存在虚析构函数,那么虚析构函数存在的作用是什么呢,我们可以先来一段实例演示一下。
#include <iostream>
#include <string>
using namespace std;
class father{
public:
virtual void func()=0;
~father(){
cout<<"这是父类的析构函数"<<endl;
}
};
class son:public father{
public:
void func(){
cout<<"这是子类的函数调用"<<endl;
}
~son(){
cout<<"这是子类的析构函数"<<endl;
}
};
void test(){
father *f=new son;
f->func();
cout<<"son类的大小"<<sizeof(son)<<endl;
cout<<"father类的大小"<<sizeof(father)<<endl;
delete f;
}
int main(){
test();
return 0;
}
我们可以发现,当对象调用完理应会调用其析构函数,但是这里显然可以看到只调用了父类的析构函数,但是并没有调用子类的析构函数,带来的后果就是当调用类时,在内部开辟了堆的空间,一般我们需要在析构函数中释放掉,但这里没释放掉会导致内存泄漏的问题,改善的方法就是用虚析构函数或者纯虚析构函数,举例如下。
#include <iostream>
#include <string>
using namespace std;
class father{
public:
virtual void func()=0;
virtual ~father(){
cout<<"这是父类的析构函数"<<endl;
}
int *num;
};
class son:public father{
public:
son(int a){
this->num=new int (a);
}
void func(){
cout<<"这是子类的函数调用"<<endl;
}
~son(){
if(num!=NULL){
cout<<"这是子类的析构函数"<<endl;
delete num;
num=NULL;
}
}
};
void test(){
father *f=new son(20);
f->func();
cout<<"该数值是:"<<*f->num<<endl;
cout<<"son类的大小"<<sizeof(son)<<endl;
cout<<"father类的大小"<<sizeof(father)<<endl;
delete f;
}
int main(){
test();
return 0;
}
可以看到这里调用了虚析构函数后,就能够调用到子类的析构函数了,那纯虚析构该如何写呢,和纯虚函数是类似的写法
纯虚析构函数语法:virtual ~类名()=0;
然后在类外需要实现函数体:类名::~类名(){ }
这里需要注意即使类中没有纯虚函数,但是存在纯虚析构函数该类也被称之为抽象类