【C/C++】【类和对象】多态和虚函数

基类指针/派生类指针

#include <iostream>


using namespace std;





class Human
{
public:
	Human();
	Human(int);

public:
	int m_Age;
	char m_Name[100];


public:
	void func_human();
};

Human::Human()
{
	cout << "Human::Human()" << endl;
}


Human::Human(int tmp)
{
	cout << "Human::Human(int tmp)" << endl;
}


void Human::func_human()
{
	cout << "void Human::func_human()" << endl;
}


//Men是Human的子类
class Men:public Human
{
public:
	Men();
	Men(int);

private:

public:
	void func_man();
};

Men::Men()
{
	cout << "Men::Men()" << endl;
}


Men::Men(int tmp)
{
	cout << "Men::Men(int tmp)" << endl;
}

void Men::func_man()
{
	cout << "void Men::func_man()" << endl;
}
int main()
{

	Human* p_human = new Human();
	Men* p_men = new Men();

	//父类指针可以new子类对象,父类指针指向子类对象;反之不行;
	Human* p_h = new Men();
	p_h->func_human(); //父类类型指针调用父类的成员函数;
	//p_h->func_man(); //错误,不可以,虽然是new子类对象,但是你是父类指针,不可以调用子类成员函数

	//Men* p_m = new Human(); 子类指针无法父类对象;
	
	//父类指针没有办法调用子类的成员函数,为什么还让父类指针new一个子类对象呢???
    //借助虚函数

}

虚函数

#include <iostream>

using namespace std;

class Human
{
public:
	Human();
	Human(int);

public:
	int m_Age;
	char m_Name[100];


public:
	void func_human();
	virtual void eat();
};

Human::Human()
{
	cout << "Human::Human()" << endl;
}


Human::Human(int tmp)
{
	cout << "Human::Human(int tmp)" << endl;
}


void Human::func_human()
{
	cout << "void Human::func_human()" << endl;
}

void Human::eat()
{
	cout << "human eat food" << endl;
}

//Men是Human的子类
class Men:public Human
{
public:
	Men();
	Men(int);

private:

public:
	void func_man();
	virtual void eat();
};

Men::Men()
{
	cout << "Men::Men()" << endl;
}


Men::Men(int tmp)
{
	cout << "Men::Men(int tmp)" << endl;
}

void Men::func_man()
{
	cout << "void Men::func_man()" << endl;
}

void Men::eat()
{
	cout << "men eat mi" << endl;
}


//Women是Human的子类
class Women :public Human
{
public:
	Women();
	Women(int);

private:

public:
	void func_women();
	virtual void eat();
};

Women::Women()
{
	cout << "Women::Women()" << endl;
}


Women::Women(int tmp)
{
	cout << "Women::Women(int tmp)" << endl;
}

void Women::func_women()
{
	cout << "void Women::func_women()" << endl;
}

void Women::eat()
{
	cout << "women eat miantiao" << endl;
}


int main()
{

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

	//1. 比较繁琐,定义多个指针来解决
	Men* p_m = new Men();
	p_m->eat(); //调用子类men的eat


	Women* p_w = new Women();
	p_w->eat(); //调用子类women的eat
	*/

	//2. 定义一个对象指针,就可以调用父类和各个子类的同名,同参函数
	// 1). 这个对象指针,必须是父类指针;
	// 2). 这个同名同参函数,父类中声明之前必须要加virtual;声明为虚函数;子类中不加也可以,一旦某个函数在父类中声明虚函数,子类继承后都是虚函数
	//     子类中添加virtual可以增加可读性;函数重写,子类和父类的函数名、参数、返回值一样;
	/*
	Human* p_h = new Men();
	p_h->eat(); //调用Men的虚函数,new的是什么调用什么;
	delete p_h;
	

	p_h = new Women();
	p_h->eat(); //调用的是Women的虚函数
	delete p_h;


	p_h = new Human();
	p_h->eat(); //调用的是Human的虚函数
	delete p_h;
	*/

    //3. 不重新new,直接调用Human的虚函数;
	/*
	Human* p_h_1 = new Men();
	p_h_1 -> eat(); //调用Men的虚函数
	p_h_1->Human::eat(); //调用的是Human的虚函数
	delete p_h_1;
	*/
	
}

override

为了避免在子类中写错虚函数,在C++11中,可以在函数声明中添加override关键字;这个关键字用在子类中,而且是虚函数专用;override就是用来说明派生类中的虚函数,用了这个关键字,编译器会认为这个eat是覆盖了基类中的同名函数(只有虚函数才存在子类可以覆盖父类中同名函数的问题);那么编译器就会在父类中找同名同参的虚函数,如果没找到,编译器就会把报错;如果函数名,参数返回值写错了,编译器会帮忙纠错;

注意: 派生类的虚函数可以和基类的虚函数的返回值有差别;

final

也是虚函数专用,用在父类中,如果在父类的函数声明中加了final关键字;子类不能覆盖父类的该函数;

调用虚函数执行的还是“动态绑定”,动态:指的是在程序运行的时候才知道调用哪个函数;

多态性

只是针对虚函数来说的,随着虚函数的提出,面向对象的三大特性之一:多态性;

多态性:体现在具有继承关系的父类和子类之间,子类重新定义父类的成员函数,同时父类把该成员函数声明为virtual

通过父类的指针,只有到了程序运行的时候,找到动态绑定到父类指针上的对象,这个对象它有可能是某个子类对象,也可能是父类对象

然后系统内部实际上要查一个虚函数表,找到函数的入口地址,从而调用父类或者子类的函数;这就是运行时期的多态性;

纯虚函数

纯虚函数是在基类中声明的虚函数,但是在基类中没有定义;但是要求任何派生类都要定义该虚函数的实现方法;

基类中实现纯虚函数的方法是在函数原型后增加=0

注意:

  1. 成员函数有纯虚函数的类叫抽象类,不能用来生成对象,主要用来当做基类来生成子类用;
  2. 子类必须要完全实现该基类中定义的纯虚函数;
#include <iostream>


using namespace std;





class Human
{
public:
	Human();
	Human(int);

public:
	int m_Age;
	char m_Name[100];


public:
	virtual void eat() =0;
};

Human::Human()
{
	cout << "Human::Human()" << endl;
}


Human::Human(int tmp)
{
	cout << "Human::Human(int tmp)" << endl;
}




//Men是Human的子类
class Men:public Human
{
public:
	Men();
	Men(int);

private:

public:
	virtual void eat();
};

Men::Men()
{
	cout << "Men::Men()" << endl;
}


Men::Men(int tmp)
{
	cout << "Men::Men(int tmp)" << endl;
}


void Men::eat()
{
	cout << "men eat mi" << endl;
}


//Women是Human的子类
class Women :public Human
{
public:
	Women();
	Women(int);

private:

public:

	virtual void eat();
};

Women::Women()
{
	cout << "Women::Women()" << endl;
}


Women::Women(int tmp)
{
	cout << "Women::Women(int tmp)" << endl;
}



void Women::eat()
{
	cout << "women eat miantiao" << endl;
}


int main()
{
	//抽象类,不能用来生成对象,主要用来统一管理子类对象
	//Human h; //一旦一个类中有纯虚函数,不能生成这个类的对象了,不能实例化抽象类
	//Human* p_h = new Human; 不合法


	Human* p_m = new Men;
	p_m->eat();
	Human* p_w = new Women;
	p_w->eat();
	
}

虚析构函数

基类的析构函数一般写成虚函数;

#include <iostream>


using namespace std;





class Human
{
public:
	Human();
	Human(int);
	virtual ~Human();
public:
	int m_Age;
	char m_Name[100];


public:
	virtual void eat() =0;
};

Human::Human()
{
	cout << "Human::Human()" << endl;
}
Human::~Human()
{
	cout << "Human::~Human()" << endl;
}

Human::Human(int tmp)
{
	cout << "Human::Human(int tmp)" << endl;
}




//Men是Human的子类
class Men:public Human
{
public:
	Men();
	Men(int);
	~Men();
private:

public:
	virtual void eat();
};

Men::Men()
{
	cout << "Men::Men()" << endl;
}
Men::~Men()
{
	cout << "Men::~Men()" << endl;
}

Men::Men(int tmp)
{
	cout << "Men::Men(int tmp)" << endl;
}


void Men::eat()
{
	cout << "men eat mi" << endl;
}


//Women是Human的子类
class Women :public Human
{
public:
	Women();
	Women(int);
	~Women();
private:

public:

	virtual void eat();
};

Women::Women()
{
	cout << "Women::Women()" << endl;
}
Women::~Women()
{
	cout << "Women::~Women()" << endl;
}

Women::Women(int tmp)
{
	cout << "Women::Women(int tmp)" << endl;
}



void Women::eat()
{
	cout << "women eat miantiao" << endl;
}


int main()
{
	//Men men;

	Human* p_m = new Men;
	delete p_m; //没有调用子类的析构函数 

	//总结:用基类指针 new子类的对象,在delete的时候,系统不会调用派生类的析构函数
	//解决方法:
	//把父类的析构函数写成虚函数
	//在public继承中,基类对派生类以及起对象的操作,只能影响到哪些从基类继承下来的成员;
	//如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数,析构函数也是这样;
	//另外,基类中析构函数的虚属性也会被继承给子类,这样的话子类中的析构函数自然就成了虚函数,虽然名字跟基类的析构函数不同;
	//detete puhuan; 肯定要调用父类的析构函数,但是在父类析构函数中它要想调用子类men的析构函数,那么
	//human这个类的析构函数就要声明成virtual,也就是C++中的运行时多态,调用的函数必须是virtual的;
	//结论
	//如果一个类,想要做基类,一定要把这个类的析构函数写成虚析构函数;
	//只要基类的析构函数是虚函数,能保证delete基类指针时候正确的运行析构函数版本;

	//普通类不写析构函数,但是如果是基类,必须是虚析构函数;

}
上一篇:C++高级编程之抽象类与抽象类界面


下一篇:Go语言基础、实战 -- 7. 指针