C++继承中的基类指针、派生类指针和虚函数

在C++中,继承是面向对象程序设计的核心思想之一,它构建了类之间的层次关系,有父类和子类的概念。本文将总结基类指针、派生类指针和虚函数的相关内容,并附带相应的示例代码。

一、基类指针、派生类指针

在C++中,父类指针可以new一个子类对象,但子类指针不能new一个父类对象父类指针可以调用父类的成员函数,但无法调用子类的成员函数,因为父类指针只能访问父类的成员变量和成员函数。

//定义基类/父类/超类
//Human.h文件
//定义基类/父类/超类

#ifndef _HUMAN_
#define _HUMAN_

#include<iostream>

class Human
{
public:
	Human();
	virtual ~Human();
	Human(int);
public:
	//virtual void eat() /*final*/;//声明成虚函数

	virtual void eat2() = 0;//纯虚函数,没有函数体只有一个函数声明
public:
	int m_Age;//年龄
	char m_Name[100];//名字
	void funchuman() {};

	void funcpub()
	{
		std::cout << "执行了Human::funcpub()" << std::endl;
	};
public:
	void samenamefunc();
	void samenamefunc(int);
protected:
	int m_pro1;
	void funcpro() 
	{
		std::cout << "执行了Human::funcpro()" << std::endl;
	};
private:
	int m_priv1;
	void funcpriv() {};


};//类定义或者类声明时大家千万不要忘记末尾的;
#endif // _HUMAN_

//Human.cpp文件    Human.cpp文件    Human.cpp文件
#include"Human.h"
#include<iostream>

//Human类的实现

Human::Human()//不带参数的构造函数(默认构造函数)
{
	std::cout << "执行了Human::Human()构造函数" << std::endl;
}

Human::~Human()
{
	std::cout << "执行了Human::~Human()析构函数" << std::endl;
}

Human::Human(int abc)//带一个参数的构造函数
{
	std::cout << "执行了Human::Human(int abc)构造函数" << std::endl;
}

void Human::samenamefunc()
{
	std::cout << "执行了Human::samenamefunc()" << std::endl;
}

void Human::samenamefunc(int)
{
	std::cout << "执行了Human::samenamefunc(int)"<<std::endl;
}

//void Human::eat()//声明成虚函数
//{
//	std::cout << "人类吃各种粮食" << std::endl;
//}

//Men.h文件    Men.h文件   Men.h文件
//定义Human的子类Men
//定义Human的子类Men

#ifndef _MEN_
#define _MEN_

#include"Human.h"
#include<iostream>
//男人
class Men : public Human//表示Men是Human的子类
	//class Men : protected Human
	//class Men : private Human
{
public:
	Men();
	~Men();

public:
	void funcmen() {};
	void samenamefunc(int);

public:
	using Human::samenamefunc;
public:
	//virtual void eat() override;
	virtual void eat2() 
	{
		std::cout << "Men::eat2()"<<std::endl;
	};

};//类定义或者类声明时大家千万不要忘记末尾的;
#endif // _MEN_

//MEN.cpp   MEN.cpp   MEN.cpp
#include"Men.h"
#include<iostream>

//子类Men的实现
Men::Men()
{
	//funcpro();
	std::cout << "执行了Men::Men()构造函数"<<std::endl;
}

Men::~Men()
{
	std::cout << "执行了Men::~Men()析构函数" << std::endl;
}

void Men::samenamefunc(int)
{
	//Human::samenamefunc(12);//强制调用父类的同名函数
	//Human::samenamefunc();//强制调用父类的同名函数
	std::cout << "执行了void Men::samenamefunc(int)" << std::endl;
}

//void Men::eat()
//{
//	std::cout << "男人喜欢吃米饭" << std::endl;
//}

//主文件.cpp   主文件.cpp  主文件.cpp
#include<iostream>
#include"Human.h"
#include"Men.h"
#include"Women.h"

int main()
{
    Human* phuman = new Men; // 可以编译
    Men* pmen = new Human; // 报错,编译不通过
    phuman->funchuman(); // 父类类型指针可以调用父类的成员函数
    phuman->funcmen(); // 报错,无法调用子类成员函数
    return 0;
}

概念引入:既然父类指针没有办法调用子类的成员函数,那么你为什么还要父类指针new一个子类对象呢?

二、虚函数

虚函数是在父类中声明的子类可以重写该函数。在调用虚函数时,会动态绑定,即在程序运行时才能确定调用哪个子类的函数。在父类指针调用虚函数时,会调用子类的同名函数。

    Human* phuman = new Human;
	phuman->eat();//调用的是父类的eat函数,因为phuman是父类指针。
	那如何调用Men和Women的eat函数呢?

	Men* pmen = new Men;
	pmen->eat();//调用子类Men的eat()

	Women* pwomen = new Women;
	pwomen->eat();//调用子类Women的eat()
	以上方法太**麻烦**,有没有一个**解决方法**,我们**只定义一个对象指针**,就能够**调用父类**,以及**各个子类的同名函数**呢?
	  有:**这个对象指针,它的类型必须是父类类型(Human *phuman)。**
	我们如果想通过一个父类指针调用父类,子类中的同名同参的函数的话,对于这个函数的要求:
	  在父类中,**函数(eat()函数)声明之前必须要加virtual声明函数(eat()函数)成虚函数**
	  **一旦某个函数(在基类中)被声明成了虚函数,,在派生类(子类)中也被声明成了虚函数**
	  
	Human* phuman1 = new Men;
	phuman1->eat();//调用的是父类的eat();

	eat()声明成虚函数
	Human* phuman = new Men;
	phuman->eat(); //调用的是子类Men的eat()函数
	phuman->Human::eat();//调用父类的eat()函数
	delete phuman;
	如果未声明成虚函数:
	  Human* phuman = new Men;//可以编译
	  phuman->eat();//调用的是父类(Human)的eat()函数,因为phuman是父类指针。

	phuman = new Women;
	phuman->eat();//调用的是子类Women的eat()函数
	delete phuman;

	phuman = new Human;
	phuman->eat();//调用的是父类Human的eat()函数
	delete phuman;

三、override和final关键字

在C++11中,可以在函数声明中加入override关键字,用于说明派生类中的虚函数覆盖了父类的同名函数如果子类中的虚函数写错了名字或参数,编译器会报错。

**为了避免你在子类类中写错虚函数(如:参数不一致)**,在C++11中,你可以**在函数声明这里增加一个override关键字**,这个关键字,**用在“ 子类 ”中,而且虚函数专用。**

**override**就是用来说明 **派生类** 中的 **虚函数** ,你用了这个关键字之后,编译器就会认为你这个eat是覆盖了父类的同名函数**(只有虚函数才存在子类可以覆盖父类中同名函数的问题)**,那么这个编译器就会在父类中找同名同参的虚函数,如果没找到,编译器就会报错,这样,如果你不小心在子类中把虚函数写错了名字,写错了参数,编译器能够帮助你纠错

finalfinal 也是 虚函数 专用,但它是用在“ 父类” 中,如果我们在父类的函数声明中加了final,那么任何尝试覆盖该函数的操作都将引发错误

调用虚函数执行的是**" 动态绑定 " 动态**:表示在我们程序运行的时候才能知道调用了哪个子类的eat()虚函数。
动态的绑到Men上去,绑到Women上去,取决于new的是Men 还是 Wonmen

总结:
动态绑定:运行的时候才决定你的phuman对象绑到到哪个eat()函数上运行;
Men men;
men.eat();//调用肯定是Men;
Women women;
women.eat();//调用的肯定是Wonmen
Human human;
human.eat();//调用的肯定是Human

虚函数一般是用指针来操作,不然虚函数作用体现不出来

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值