虚函数是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......)