C++-Record38—多态概念的引入及案例分析

目录

多态的意义

多态的问题抛出

实现多态的解决方案

总体代码


多态的意义

多态是设计模式和编写框架的一个基础,都知道的面向对象的三大概念,封装,继承和多态,其实,与其说是三大概念,不如说是三种境界,比如,封装的意义在于,突破了C语言中函数的概念,当把一个类做函数参数的时候,我们把一个类的对象传到一个被调用函数里,这样在被调用函数里面,可以直接使用这个类的属性和类的方法;再看继承,它的意义在于,继承可以复用以前人写的代码,这样就可以解决代码复用的一些难题了;最后,看多态,多态应该比继承更牛,这是因为,多态不光是继承以前人写的代码,更重要的是,多态可以复用后来人写的代码,即一个八十年代人写的代码,可以复用九十年代人写的代码,可以扩展新的标准,从这个角度看,多态应该是一个更高层次上的思想。

多态的问题抛出

赋值兼容性原则遇上函数重写,即如果子类定义了与父类中原型相同的函数会发生什么?

先定义一个父类函数:

class Parent
{
public:
	Parent(int a)
	{
		this->a = a;
		cout<<"Parent a"<<a<<endl;
	}
protected:
private:
	int a ;
};

 再定义一个子类:

class Child : public Parent
{
public:
	Child(int b) : Parent(10)
	{
		this->b = b;
		cout<<"Child b"<<b<<endl;
	}
protected:
private:
	int b;
};

这样构造关系就有了,再这个场景之下,我们再写一个print函数,再父类和子类中,分别添加print函数,在父类中添加函数:

	void print() //子类的和父类的函数名字一样
	{
		cout<<"Parent 打印 a:"<<a<<endl;
	}

在子类中添加函数:

	void print() //virtual 父类写了virtual,子类可写 可不写 
	{
		cout<<"Child 打印  b:"<<b<<endl;
	}

 我们知道根据赋值兼容性原则,可以将子类的对象,赋给基类的指针,那么我们就直接定义一个基类的指针,

void main()
{
	Parent	*base = NULL;
	Parent	p1(20);
	Child	c1(30);

	base = &p1;
	base->print(); //执行父类的打印函数

	base = &c1;
	base->print(); //执行谁的函数 ?  //面向对象新需求

	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

那么,当"base = &c1;"的时候,执行的print函数是谁的?先看看代码运行结果:

 发现,最终执行的是父类的print函数,而不是子类的。这里就存在一个新的需求,如何来调用子类的print函数,总不能每次都去调用父类的吧!

再接着看,在主调函数中添加,新的代码段,采用引用的方式再试:

	{
		Parent &base2 = p1;
		base2.print();

		Parent &base3 = c1; //base3是c1 的别名
		base3.print();
	}

 在这种场景下,base3就是c1,那再进行打印,看看调用的是谁的print函数:

发现调用的print函数仍旧是父类的,竟然连别名,都不别名了!

还是不服气,再试,用指针做函数参数的方式再来调用,添加一个新的函数:

void howToPrint(Parent *base)
{
	base->print(); //一种调用语句 有多种表现形态...
}

void howToPrint2(Parent &base)
{
	base.print();
}

分别对这两次函数,做一次函数调用:

	//函数调用
	howToPrint(&p1);
	howToPrint(&c1);

	howToPrint2(p1);
	howToPrint2(c1);

发现结果和之前还是一样,还是一直调用的是父类的print函数,就每调用过子类的:

 其实,我们实现的功能很简单,传来一个子类的指针对象,就执行子类的print函数,传来一个父类的指针对象,就执行父类的print函数,但经过以上尝试,发现都不行,而这就是面向对象的新需求!

下面进行理论总结:

编译器的做法不是我们期望的:

        根据实际的对象类型来判断重写函数的调用,如果父类指针指向的是父类对象则调用父类中定义的函数,如果父类指针指向的是子类对象则调用子类中定义的重写函数。

我们期望的:

对同样的调用语句有多种的不同的表现形式,像这种表现形式,我们就称之为多态!

实现多态的解决方案

解决方案很简单,就是加"virtual"关键字,强调的地方有两点:1)这才是"virtual"关键字,真正的主流应用场景!之前在虚继承中的应用并不是;2)如果在父类的函数中加上了"virtual"关键字,在子类中的相同函数加不加这个关键字(一般情况下都写上,表示醒目),都无所谓,都能达到实现多态的功能。

将之前的代码进行改进,在父类的print函数前加"virtual"关键字:

	virtual void print() //子类的和父类的函数名字一样
	{
		cout<<"Parent 打印 a:"<<a<<endl;
	}

 在子类的print函数前加"virtual"关键字:


	virtual void print() //virtual 父类写了virtual,子类可写 可不写 
	{
		cout<<"Child 打印  b:"<<b<<endl;
	}

 这个时候,在进行调用就可以了!

void main()
{
	Parent	*base = NULL;
	Parent	p1(20);
	Child	c1(30);

	base = &p1;
	base->print(); //执行父类的打印函数

	base = &c1;
	base->print(); //执行谁的函数 ?  //面向对象新需求
	
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

这样都是通过"base->print()"来调用的,就产生了不太的效果:

这样的话,运行之前的存放在主调中的三种尝试代码,都实现了想要的功能:

 这样的就做到了一个调用语句有多种表现形态!这就是C++给我们提供的多态。

总体代码

dm13_类型兼容性原则遇上函数重写.cpp


#include <iostream>
using namespace std;

class Parent
{
public:
	Parent(int a)
	{
		this->a = a;
		cout<<"Parent a"<<a<<endl;
	}

	virtual void print() //子类的和父类的函数名字一样
	{
		cout<<"Parent 打印 a:"<<a<<endl;
	}
protected:
private:
	int a ;
};

class Child : public Parent
{
public:
	Child(int b) : Parent(10)
	{
		this->b = b;
		cout<<"Child b"<<b<<endl;
	}
	virtual void print() //virtual 父类写了virtual,子类可写 可不写 
	{
		cout<<"Child 打印  b:"<<b<<endl;
	}
protected:
private:
	int b;
};

void howToPrint(Parent *base)
{
	base->print(); //一种调用语句 有多种表现形态...
}

void howToPrint2(Parent &base)
{
	base.print();
}
void main()
{
	
	Parent	*base = NULL;
	Parent	p1(20);
	Child	c1(30);

	base = &p1;
	base->print(); //执行父类的打印函数

	base = &c1;
	base->print(); //执行谁的函数 ?  //面向对象新需求


	{
		Parent &base2 = p1;
		base2.print();

		Parent &base3 = c1; //base3是c1 的别名
		base3.print();
	}


	//函数调用
	howToPrint(&p1);
	howToPrint(&c1);

	howToPrint2(p1);
	howToPrint2(c1);



	
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值