20 C++ 多态,override,final,纯虚函数,抽象类,基类析构函数写成虚函数

多态成立的条件

有继承

父类指针指向子类对象或者父类引用指向子类对象

父类和子类有一模一样的方法(返回值,方法名,方法参数),且该方法用virtual 修饰- 我们称之为虚函数

//十六.虚函数以及多态相关 
//参考parent3.h ,child31,child32
//父类指针指向子类对象或者父类引用指向子类对象。
//父类和子类中有相同的函数,(返回值,方法名, 参数列表完全一样)
//在相同的函数前面加上关键字virtual,就可以实现多态。
//加了virtural关键字的方法就是虚方法。
//注意 virtual 关键字一定是要加在 父类.h 的方法上,父类的.cpp上不加,子类上可加可不加。
 

parent3.h

#pragma once
#include 
#include 

using namespace std;

class Parent3 {

public:
	int m_parent_age;
protected:
	string m_parent_name;
private:
	double m_parent_number;

public:
	//构造方法
	Parent3(int age, string name, double number);

public:
	//虚方法,多态的方法
	virtual void eat();

public:
	//parent3独自拥有的public方法
	void parentpubliceat();

protected:
	//parent3独自拥有的 protected 方法
	void parentprotectedeat();

private:
	//parent3独自拥有的 private 方法
	void parentprivateeat();

};

parent3.cpp

#include "parent3.h"

Parent3::Parent3(int age, string name, double number):m_parent_age(age),m_parent_name(name),m_parent_number(number)
{
	cout << "Parent3 构造方法被执行" <<  endl;
}

//想要实现多态的函数
void Parent3::eat()
{
	cout << "Parent3 eat方法被执行" << endl;
}

void Parent3::parentpubliceat()
{
	cout << "Parent3 自己的public eat 方法" << endl;
}

void Parent3::parentprotectedeat()
{
	cout << "Parent3 自己的 protected eat 方法" << endl;
}

void Parent3::parentprivateeat()
{
	cout << "Parent3 自己的 private eat 方法" << endl;
}

child31.h

#pragma once
#include 
#include 
#include "parent3.h"
using namespace std;

class Child31 :public Parent3{

public:
	int m_31childage;
	string m_31childname;
	double m_31childnumber;
public:
	Child31(int childage,string childname,double childnumber,int parentage,string parentname,double parentnumber);
	//以防止写错,可以使用override 关键字,使用后,如果父类没有eat方法,会有build error
	void eat() override;
public:
	void child31publiceat();
protected:
	void child31protectedeat();
private:
	void child31privateeat();
};

child31.cpp

#include "child31.h"


Child31::Child31(int childage, string childname, double childnumber, 
	int parentage, string parentname, double parentnumber)
	:Parent3(parentage, parentname, parentnumber),
	m_31childage(childage),m_31childname(childname),m_31childnumber(childnumber)
{
	cout << "Child31构造方法执行" << endl;
}

void Child31::eat()
{
	cout << "Child31 eat 方法执行" << endl;
}

void Child31::child31publiceat()
{
	cout << "Child31 自己的 public 方法执行" << endl;
}

void Child31::child31protectedeat()
{
	cout << "Child31 自己的 protected 方法执行" << endl;
}

void Child31::child31privateeat()
{
	cout << "Child31 自己的 priavte 方法执行" << endl;
}

child32.h

#include "parent3.h"

using namespace std;

class Child32 :public Parent3 {

public:
	int m_32childage;
	string m_32childname;
	double m_32childnumber;
public:
	Child32(int childage, string childname, double childnumber, int parentage, string parentname, double parentnumber);

	void eat() override;

public:
	void child32publiceat();
protected:
	void child32protectedeat();
private:
	void child32privateeat();
};

child32.cpp

#include "child32.h"

Child32::Child32(int childage, string childname, double childnumber, 
	int parentage, string parentname, double parentnumber)
	:Parent3(parentage, parentname, parentnumber),
	m_32childage(childage),m_32childname(childname),m_32childnumber(childnumber)
{
	cout << "Child32构造方法执行" << endl;
}

void Child32::eat()
{
	cout << "Child32 eat 方法执行" << endl;
}

void Child32::child32publiceat()
{
	cout << "Child32 自己的 public 方法执行" << endl;
}

void Child32::child32protectedeat()
{
	cout << "Child32 自己的 protected 方法执行" << endl;
}

void Child32::child32privateeat()
{
	cout << "Child32 自己的 priavte 方法执行" << endl;
}

main.cpp

#include "parent3.h"
#include "child31.h"
#include "child32.h"

void main() {

	//多态 start
	Parent3 *pa1 = new Child31(31, "child31", 99.9, 62, "parent3", 88.8);
	cout << "111111" << endl;
	pa1->eat();
	cout << "222222" << endl;
	Parent3 *pa2 = new Child32(32, "child32", 32.32, 64, "parent3", 64.64);
	cout << "333333" << endl;
	pa2->eat();
	cout << "444444" << endl;
	//多态end

	//由于这时候 pa1的定义是 Parent3,因此只能访问 Parent3 的方法,
	//且由于是main.cpp中访问(也就是说:在类外访问类),只能访问Parent3的public方法
	pa1->parentpubliceat();
	

}

override明确地表示一个函数是对基类中一个虚函数的重载

更重要的是,它会检查基类虚函数和派生类中重载函数的签名不匹配问题。如果签名不匹配,编译器会发出错误信息。

parent.h

public:
	//虚方法,多态的方法
	virtual void eat();
//想要实现多态的函数
void Parent3::eat()
{
	cout << "Parent3 eat方法被执行" << endl;
}

child.h

	//以防止写错,可以使用override 关键字,使用后,如果父类没有eat方法,会有build error
	void eat() override;
void Child31::eat()
{
	cout << "Child31 eat 方法执行" << endl;
}

如果不是 虚函数,则子类中不能使用

parent.h

public:
//不是虚函数
	void aaa();

void Parent3::aaa()
{
	cout << "parent aaa" << endl;
}

child.h

public:
	Child31(int childage,string childname,double childnumber,int parentage,string parentname,double parentnumber);
	//以防止写错,可以使用override 关键字,使用后,如果父类没有eat方法,会有build error
	void eat() override;
	void aaa() override;//build error ,aaa()方法需要是 虚函数

final 关键字,用于父类.h中,表示该类无法继承

//十七. final关键字
//final 修饰类,则类不等被继承

class Parent4 final{}


//parent4.h child4.h

#pragma once
#include 
#include 

using namespace std;

//将类声明成final,则该类不能被继承
 class Parent4 final {

public:
	int m_parent_age;
protected:
	string m_parent_name;
private:
	double m_parent_number;

public:
	//构造方法
	Parent4();
	Parent4(int age, string name, double number);

public:
	//虚方法,多态的方法
	virtual void eat();

};

#include "parent4.h"

using namespace std;

//严重性	代码	说明	项目	文件	行	禁止显示状态
//错误	C3246	“Child4” : 无法从“Parent4”继承,因为它已被声明为“final”	001for	d : \allinformation\cpp\001for\001for\001for\child4.h	5

class Child4 :public Parent4 { //build error,Parent4类被设置为final,因此不能被继承

public:
	Child4();
};

final关键字,用于父类的某一个方法中,表示该方法无法继承

parent.h

public:
//final 必须修饰 virtual 的方法,表示该方法可以被继承,但是不能被重写
	virtual void cannotextendsfunc() final;
	

parent.cpp

void Parent3::cannotextendsfunc()
{
	cout << "parent cannotextendsfunc" << endl;
}

child.h

//build error,cannotextendsfunc被声明为final,无法重写。	
//void cannotextendsfunc();

//build pass ,方法名一样,但是参数不一样,因此是child31自己写的一个新函数
//void cannotextendsfunc(int a );

main.cpp

	// final 方法可以被继承,但是不能重写
	Child31 ch31 = Child31(31, "child31", 99.9, 62, "parent3", 88.8);
	ch31.cannotextendsfunc();//该 方法可以被继承,但是不能重写,因此会调用父类的 cannotextendsfunc方法

纯虚函数 和 抽象类 :

父类中虚函数后面加上 =0 ,虚函数只有声明,可以没有定义。

    //纯虚函数 在.h中 声明,在.cpp中定义或者不定义都可以,因为子类都要实现该虚函数,且该类有了该 纯虚函数后,就变成了抽象类,抽象类无法初始化自己,因此也调用不到,这就是在.cpp中定义或者不定义都可以的原因。

parent.h
    virtual void eat() = 0;

在这里我们在parent.cpp中 定义了 该纯虚函数。实验证明,不定义该纯虚函数也是一样的。

parent.cpp

//定义纯虚函数
void Parent5::eat()
{
    cout << "Parent5 eat 方法被执行" << endl;
}

parent.h

#pragma once
#include 
#include 

using namespace std;

//如果类中有纯虚函数,该类就是抽象类,抽象类无法创建自己
 class Parent5  {

public:
	int m_parent_age;
protected:
	string m_parent_name;
private:
	double m_parent_number;

public:
	//构造方法
	Parent5();
	Parent5(int age, string name, double number);

public:
	//纯虚函数
	virtual void eat() = 0;

	void aaa();

};

parent.cpp

#include "parent5.h"

Parent5::Parent5(int age, string name, double number):m_parent_age(age),m_parent_name(name),m_parent_number(number)
{
	cout << "Parent5 构造方法被执行" <<  endl;
}

Parent5::Parent5() {
	cout << "Parent5 空构造方法被执行" << endl;
}

//定义纯虚函数
void Parent5::eat()
{
	cout << "Parent5 eat 方法被执行" << endl;
}

void Parent5::aaa()
{
	cout << "Parent5 aaa 方法被执行" << endl;
}

子类中都要定义该虚函数自己的实现方法。

child51.h

#pragma once
#include 
#include 
#include "parent5.h"
using namespace std;

class Child51 :public Parent5{

public:
	int m_51childage;
	string m_51childname;
	double m_51childnumber;
public:
	Child51();
	Child51(int childage,string childname,double childnumber,int parentage,string parentname,double parentnumber);
	//以防止写错,可以使用override 关键字,使用后,如果父类没有eat方法,会有build error
	void eat() override;

};

child51.cpp

#include "child51.h"


Child51::Child51()
{
	cout << "child51 空的构造函数 " << endl;
}

Child51::Child51(int childage, string childname, double childnumber,
	int parentage, string parentname, double parentnumber)
	:Parent5(parentage, parentname, parentnumber),
	m_51childage(childage),
	m_51childname(childname),
	m_51childnumber(childnumber)
{
	cout<<"child51 构造函数 "<

child52.h

#pragma once
#include 
#include 
#include "parent5.h"
using namespace std;

class Child52 :public Parent5 {

public:
	int m_52childage;
	string m_52childname;
	double m_52childnumber;
public:
	Child52();
	Child52(int childage, string childname, double childnumber, int parentage, string parentname, double parentnumber);
	//以防止写错,可以使用override 关键字,使用后,如果父类没有eat方法,会有build error
	void eat() override;

};

child52.cpp

#include "child52.h"

Child52::Child52()
{
	cout << "child52 空的构造函数 " << endl;
}

Child52::Child52(int childage, string childname, double childnumber,
	int parentage, string parentname, double parentnumber)
	:Parent5(parentage, parentname, parentnumber),
	m_52childage(childage),
	m_52childname(childname),
	m_52childnumber(childnumber)
{
	cout << "child52 构造函数 " << endl;
}

void Child52::eat()
{
	cout << "child52 eat 函数" << endl;
}

main.cpp

void main() {

	//build error,原因是 "Parent5::eat" 是纯虚函数
	//Parent5 pa;

	// 在 child51 没有实现 eat 虚构函数的时候,Child51 无法建立对象,会有build error
	//Child51 c51; 

	// 在 child51 没有实现 eat 虚构函数的时候,Child51 无法建立对象,会有build error
	//Parent5 *pa = new Child51();


	Child51 c51; // 在 child51 实现 eat 虚构函数后,OK
	Parent5 *pa = new Child51();// 在 child51 实现 eat 虚构函数后,OK
	pa->eat();//多态调用 child51的 eat函数

	Parent5 *pa2 = new Child52();// 在 child52 实现 eat 虚构函数后,OK
	pa2->eat();//多态调用 child52的 eat函数

}

基类的析构函数一般需要写成纯函数

parent.h

#pragma once
#include 
#include 

using namespace std;

//基类中的析构函数一般要写成 虚函数
 class Parent6  {

public:
	//构造方法
	Parent6();
	virtual ~Parent6();
public:
	//纯虚函数
	virtual void eat();
};

parent.cpp

#include "parent6.h"

Parent6::Parent6()
{
	cout << "Parent6 构造函数 " << endl;
}

Parent6::~Parent6()
{
	cout << "Parent6 析构函数 " << endl;
}

void Parent6::eat()
{
	cout << "Parent6 eat函数" << endl;
}

child.h

#pragma once
#include 
#include 
#include "parent6.h"
using namespace std;

class Child6 :public Parent6 {

public:
	Child6();
	~Child6();
	//以防止写错,可以使用override 关键字,使用后,如果父类没有eat方法,会有build error
	void eat() override;
};

child.cpp

#include "child6.h"

Child6::Child6()
{
	cout << "Child6 构造函数 " << endl;
}

Child6::~Child6()
{
	cout << "Child6 析构函数 " << endl;
}

void Child6::eat()
{
	cout << "Child6 eat函数 " << endl;
}

main.cpp

//十九 基类的析构函数一般需要写成纯函数
//parent6 和 child6
#include "child6.h"
void main() {

	Parent6 *pa = new Child6();
	pa->eat();
	delete pa;
	//在析构函数不是 虚函数时的 运行结果,我们会发现,并没有调用child6的析构函数
			//  Parent6 构造函数
			//	Child6 构造函数
			//	Child6 eat函数
			//	Parent6 析构函数
	//原因 :: 这是因为 pa 在定义的时候,就是 Parent6 *,在delete的时候,会以这个为准。
	//如果将 Parent6中的析构函数写成 虚函数,
	//那么delete 的时候,就会变成运行时状态了,会看 new 后面的对象 也就是Child6。
	//而子类的 析构函数的 调用顺序 ,是先调用子类的析构函数,然后调用父类的析构函数。
	//解决方案:: 把 父类的析构函数写成 虚函数。

	//改后的运行结果:
			//  Parent6 构造函数
			//	Child6 构造函数
			//	Child6 eat函数
			//	Child6 析构函数
			//	Parent6 析构函数

}

你可能感兴趣的:(c++)