实例代码:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//基类
//类定义/ 类声明
class Human{
public:
Human(){
std::cout << " 执行了Human::Human() " << std::endl;
}
Human(int abc){
std::cout << " 执行了Human::Human(int) " << std::endl;
}
virtual ~Human(){
}
void funcpub(){
std::cout << " 执行了Human::funcpub() " << std::endl;
}
void somenamefunc(){
std::cout << " 执行了Human::somenamefunc() " << std::endl;
}
void funchuman(){
std::cout << " 执行了Human::funchuman() " << std::endl;
}
//声明成虚函数
virtual void eat(){
std::cout << " 人类吃各种粮食 " << std::endl;
}
public:
int m_age; //年龄
char m_name[100]; //名字
}; // 类定义/ 类声明时 大家千万不要忘记末尾的;
//子类
//男人
class Men : public Human{ //表示Men 是Human的子类
public:
Men(){
std::cout << " 执行了Men::Men() " << std::endl;
}
Men(int abc){
std::cout << " 执行了Men::Men(int) " << std::endl;
}
void somenamefunc(){
Human::somenamefunc(); //调用父类somenamefunc函数
std::cout << " 执行了Men::somenamefunc() " << std::endl;
}
void funcmen(){
std::cout << " 执行了Men::funcmen() " << std::endl;
}
virtual void eat(){
std::cout << " 男人喜欢吃米饭 " << std::endl;
}
//virtual void eat2() = 0; //纯虚函数,没有函数体,只有一个函数声明
public:
using Human::somenamefunc;
};
//女人
class WoMen : public Human{ //表示Men 是Human的子类
public:
WoMen(){
std::cout << " 执行了WoMen::WoMen() " << std::endl;
}
WoMen(int abc){
std::cout << " 执行了WoMen::WoMen(int) " << std::endl;
}
void somenamefunc(){
Human::somenamefunc(); //调用父类somenamefunc函数
std::cout << " 执行了WoMen::somenamefunc() " << std::endl;
}
void funcmen(){
std::cout << " 执行了WoMen::funcmen() " << std::endl;
}
virtual void eat(){
std::cout << " 女人喜欢吃面食 " << std::endl;
}
public:
using Human::somenamefunc;
};
int main() {
//一:基类指针、派生类指针
Men men1;
Human* phuman = new Human();
Men* pmen = new Men;
//新玩法:父类指针可以new一个子类对象。父类指针很强大,很牛逼。
Human* phuman5 = new Men; //这个是可以
//Men *pmen = new Human; // 这个不可以
phuman5->funchuman(); //父类类型指针,可以调用父类的成员函数
//phuman->funcmen(); // 不可以,虽然你new子类对象,但是你是父类指针,无法调用子类成员函数。
//既然父类指针没有办法调用子类的成员函数,那么你为什么还让父类指针new一个子类对象呢?有什么用处呢?
//二:虚函数
//phuman->eat(); // 调用的是父类的eat函数,因为phuman是父类指针。
//那如何调用Men和Women中的eat()函数。
//Men *pmen = new Men;
//pmen->eat(); // 调用子类Men中的eat();
//WoMen *pwomen = new WoMen;
//pwomen->eat(); // 调用子类WoMen中的eat();
//问题:有没有一个解决办法,使我们只定义一个对象指针,就能够调用父类,以及各个子类的同名函数?
//有: 这个对象指针,它的类型必须是父类类型。
//我们如果想通过一个父类指针调用父类、子类中的同名同参的函数的话,对这个函数也是有要求:
// 在父类中,eat函数声明之前必须要加virtual声明eat()函数成虚函数。
//一旦某个函数(在基类)被声明成了虚函数,那么所有派生类(子类)中它都是虚函数。
Human *phuman1 = new Men;
phuman1->eat(); // 调用的是Men类的eat();
phuman1->Human::eat(); // 是可以调用父类的eat;
delete phuman1;
Human *phuman2 = new WoMen;
phuman2->eat(); // 调用的是WoMen类的eat();
delete phuman2;
Human *phuman3 = new Human;
phuman3->eat(); // 调用的是Human类的eat();
delete phuman3;
// override
//为了避免你在子类中写错虚函数,在C++11中,你可以在函数声明这里增加一个override关键字,
//这个关键字 用在 “子类”中,而且是虚函数专用。
//override就是用来说明派生类中的虚函数,你用了这个关键字之后,编译器就会认为你这个eat是覆盖了父类中的同名函数
//(只有虚函数才存在子类可以覆盖父类中同名函数的问题),那么编译器就会在父类中找同名同参的虚函数,如果没找到,
//编译器就会报错,这样,如果你不小心在子类中把虚函数写错了名字,写错了参数,编译器能够帮助你纠错。
// final 也是虚函数专用,是用在“父类”,如果我们在父类的函数声明中加了final,那么任何尝试覆盖该函数的操作
//都将引发错误。
//调用虚函数执行的是“动态绑定”。动态:表示的就是在我们程序运行的时候才能知道调用了哪个子类的eat()虚函数。
//动态的绑定到Men上去,绑到Women,取决于new的是Men还是Women;
//动态绑定:运行的时候才决定你的phuman 对象绑定到哪个eat()函数上运行;
Men men;
men.eat(); //调用的肯定是Men;
WoMen women;
women.eat(); //调用的肯定是WoMen;
Human human;
human.eat(); //调用的肯定是Human;
//三: 多态性
//多态性 知识针对虚函数来说的;
//随着虚函数的提出,面向对象编程里边有一个概念“多态性”,就浮出了水面。
//多态性:体现在具有继承关系的父类和子类之间,子类重新定义(重写)父类的成员函数eat(),同时父类和子类中又把
//这个eat()函数声明成了virtual虚函数。
//通过父类的指针,只有到了程序运行时期,找到动态绑定到父类指针上的对象,这个对象它有可能是某个子类对象,也可能是父类对象,
//然后系统内部实际上是要查一下虚函数表,找到函数eat()的入口地址,从而调用父类或者子类的eat()函数,这就是运行时期的多态性。
//四: 纯虚函数
//纯虚函数 是在基类中声明的虚函数,但是它在基类中没有定义,但是要求任何派生类都要定义该虚函数自己的实现方法。
//基类中实现纯虚函数的方法是在函数原型后增加 = 0 ;
//大家注意,一旦一个类中有纯虚函数了,那么你就不能生成这个类的对象了,这个类就成为了“抽象类”;
//抽象类 不能用来生成对象,主要目的是用来统一管理子类对象。
//所以,大家记住2点:
//(1) 函数纯虚函数的类 叫抽象类,不能用来生成该类对象,主要用于当做基类来生成子类用的;
//(2) 子类中必须要实现该基类中定义的纯虚函数;
//五: 基类的析构函数一般写成虚函数(虚析构函数)
//Men men2;
//Men pmen = new Men;
//delete pmen;
//Human *phuman4 = new Men;
//delete phuman4; // 没有执行子类的析构函数。坏事;
//结论:用基类指针 new 子类的对象,在delete 的收系统不会调用派生类的析构函数。这肯定就有问题了;
//如何解决:
//我们如果把父类的析构函数写成虚函数,那么大家看:
//大家记住: 在public继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员,
//如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数,析构函数自然也应该如此。
//另外就是基类中析构函数的虚属性也会被继承给子类,这样的话子类中的析构函数也就自然而然的成为了虚函数,
//虽然名字和基类的析构函数名字不同,delete phuman的时候,肯定是要调用父类的析构函数,但在父类析构函数中它
//要是想要调用子类Men的析构函数,那么 Human这个类中的析构函数就要声明为virtual的,也就是说C++中为了获得
//运行时的多态行为,所调用的成员函数必须得是virtual的。
//所以大家要记住结论:
//如果一个类,想要做基类,我们务必要把这个类的析构函数写成virtual析构函数;
//只要基类的析构函数是虚函数,就能够保证我们delet基类指针时能够运行正确的析构函数版本;
//普通类 我们可以不写析构函数, 但如果是基类,就必须是一个析构函数,而且这个析构函数还必须是虚析构函数。
system("pause");
return 0;
}