c++中的虚函数与纯虚函数

来源:互联网 发布:女士西装套裙知乎 编辑:程序博客网 时间:2024/05/22 00:54

虚函数是C++很重要的一部分,C++的多态就是靠它完成的


首先怎么让一个函数成为虚函数

很简单,在函数声明前面加上 virtual 就可以了。如:

class BaseClass{public:virtual void Func()//虚函数{printf("Func In BaseClass\n");}};


然后我们看看虚函数究竟有什么特别之处

假设我们通过继承得到一个子类:

class ChildClass1 : public BaseClass{public:void Func(){printf("Func In ChildClass\n");}};

然后再在main函数输入测试代码:

int main(){ChildClass1 child;BaseClass*  pClass = &child;pClass->Func();        return 0;}

看看结果:

明明是BaseClass* 类型的 pClass调用的居然是ChildClass1类的方法


当然你可能认为正很正常,毕竟你是把 ChildClass1 类型的child 的地址复制给了pClass

但如果Func不是虚函数呢?

#include <stdio.h>class BaseClass{public:void Func() //注意:现在Func不是虚函数{printf("Func In BaseClass\n");}};class ChildClass1 : public BaseClass{public:void Func(){printf("Func In ChildClass\n");}};int main(){ChildClass1 child;BaseClass*  pClass = &child;pClass->Func();        return 0;}

结果却是

之后我们来看看虚函数的用处

1.将析构函数声明为虚函数

#include <stdio.h>class BaseClass{public:virtual ~BaseClass()//析构函数是虚函数{printf("Func In BaseClass\n");}};class ChildClass1 : public BaseClass{public:~ChildClass1(){printf("Func In ChildClass\n");}};int main(){ChildClass1* child = new ChildClass1();BaseClass*  pClass = child;delete pClass;        return 0;}

我们会的到结果:

看我们 delete 掉 BaseClass* 类型的 pClass ,因为 pClass  的值是由 ChildClass1* 类型的 child 复制得到的,所以会调用ChildClass1的析构函数(就是说他不只调用了父类的析构函数,也会调用子类的析构函数)


如果不是虚函数呢?

#include <stdio.h>class BaseClass{public:~BaseClass() //注意:现在不是虚函数{printf("Func In BaseClass\n");}};class ChildClass1 : public BaseClass{public:~ChildClass1(){printf("Func In ChildClass\n");}};int main(){ChildClass1* child = new ChildClass1();BaseClass*  pClass = child;delete pClass;        return 0;}


结果只调用了父类的析构函数


有时候我们会在子类里new一些变量出来,很多时候都是在子类的析构函数里才把他们delete掉

而如果你new一个这样的子类出来,然后将它转型为父类指针delete掉,就需要将基类的析构函数声明为虚函数。

(就像我等下说的学校职工管理系统的例子,就需要用delete基类指针的方式释放new出来的子类)


2.我们可以用一个父类指针数组管理多个不同的子类(当然,实际编程中多数用vector容器)

#include <stdio.h>class BaseClass{public:virtual ~BaseClass() {printf("~BaseClass\n");}virtual void Func(){printf("Func In BaseClass\n");}};class ChildClass1 : public BaseClass{public:~ChildClass1(){printf("~ChildClass1\n");}void Func(){printf("Func In ChildClass1\n");}};class ChildClass2 : public BaseClass{public:~ChildClass2(){printf("~ChildClass2\n");}void Func(){printf("Func In ChildClass2\n");}};int main(){BaseClass* classGroup[2];classGroup[0] = new ChildClass1();classGroup[1] = new ChildClass2();for(int i = 0; i<2 ; i++)classGroup[i]->Func();for(int j = 0; j<2 ; j++)delete classGroup[j];return 0;}

结果得到:

也就是说,当我们有很多个不同的子类需要统一管理的时候,虚函数就有了大用处。


具体的例子就是你为学校写了个职工管理系统,尽管学校里面的职工有很多种(老师、保安、清洁员、饭堂阿姨......)你每一种写了一个类,但都继承于Employee这个类

class Employee  {  public:        //干活, “=0“ 是纯虚函数的意思,我等下讲      virtual Work() = 0;        //拿工资      virtual GetSalary() = 0;  }; class 老师类 : public Employee{...};class 保安类 : public Employee{...};class 保洁员类 : public Employee{...};

于是你就只需要来一个 Employee数组 或者 Employee vector容器,每多一个职工就对应着new出一个具体的子类实例。将所有不同的子类实例都放进去,然后来一个循环让他们干活,再来一个循环然他们拿工资

你可以这样做:

std::vector<Employee*> group;Employee* pNewEmployee;//新来一个老师pNewEmployee = new 老师类();group.push_back(pNewEmployee);//新来一个保安pNewEmployee = new 保安类();group.push_back(pNewEmployee);//新来一个保洁员pNewEmployee = new 保洁员类();group.push_back(pNewEmployee);
至于循环那里我就不说了,自己可以想想

最后讲讲纯虚函数

纯虚函数就是一种特殊的虚函数,它拥有虚函数的所用功能(当然也有不同于虚函数的地方)

我们这样将一个函数声明为纯虚函数:

class AClass{public:virtual void Func() = 0;};
(也就是虚函数后面多一个 ” = 0 “而已)

如果一个类中有纯虚函数,那么这个类就不能被实例化

也就是:

class AClass{public:virtual void Func() = 0;};int main(){//会报错,编译不通过AClass a; //也会报错,编译不通过AClass* b = new AClass(); return 0;}

必须有一个子类实现他的纯虚函数,才能被实例化

class AClass{public:virtual void Func() = 0;};class ChildClass :public AClass{public:void Func() {}};int main(){//会报错,编译不通过//AClass a; //也会报错,编译不通过//AClass* b = new AClass(); //这样才能编译通过ChildClass a; ChildClass* b = new ChildClass(); return 0;}

值得一提的是子类必须实现父类的所有纯虚函数才能被实例化

class AClass{public:virtual void Func() = 0;};class ChildClass :public AClass{public:void Func(); //如果只有声明而没有实现它};int main(){//编译不通过ChildClass a; ChildClass* b = new ChildClass(); return 0;}

class AClass{public:virtual void Func() = 0;};class ChildClass :public AClass{public://或者你连声明都没有。。。};int main(){//编译不通过ChildClass a; ChildClass* b = new ChildClass(); return 0;}

PS:本人喜欢用 printf 多于 cout(因为现在学c++都不怎么打控制台程序了,C语言时打习惯了printf......)

0 0
原创粉丝点击