面向对象---多态的基础知识点

一、多态的概念
多态通俗来说,就是多种形态,完成某个行为,当不同的对象去完成某个行为时会产生不同的状态。
二、多态的定义及实现
1、多态定义的构成条件
多态是在不同继承关系类对象,去调用同一函数产生了不同行为

例如在火车站买票时,学生和成人分别去买票,学生买的是半价票,成人买的是全价票。同样是买票,但是不同的对象去买票时产生了不同的行为。

在继承时要构成多态还有两个重要的条件:

(1)调用函数的对象必须是指针或者引用;

(2)被调用的函数必须是虚函数,且完成了虚函数的重写。

首先解释一下第一个条件:

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
};
class Student :public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-半价" << endl;
	}
};
void Func(Person p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	system("pause");
	return 0;
}

观察上面这段代码,发现调用函数的对象不是指针或者引用,当把ps对象传过去时,p是对ps的一份拷贝;当把st对象传过去时,p是对st的切片,也就是只把父类的那一部分切了过去,根本上还是父类对象在调用。并不是不同继承关系的类对象,调用同一函数。可以看下面输出代码,发现买的都是全价票。

对于为什么必须要传指针或者引用,我简单说一下,传指针是因为,传参的时候若传的是父类对象,指针则指向父类对象,若是子类,就指向子类对象;而引用是对象的别名,传哪个对象就是哪个对象的别名。

总结一下:构成多态----->和对象有关;不构成多态----->跟类型有关。

其次关于虚函数我做以下介绍:

(1)虚函数的概念

虚函数就是在成员函数前面加上virtual关键字。

(2)虚函数的重写

在派生类中有一个跟基类完全相同的虚函数,我们就称子类的虚函数重写了基类的虚函数,完全相同是指:函数名、参数、返回值都相同。另外虚函数的重写也叫虚函数的覆盖。

(3)虚函数重写的例外

重写虚函数的返回值可以不同,但是分别必须是基类指针和派生类指针或者基类引用和派生类引用。

(4)不规范的函数重写行为

在派生类中重写的成员函数可以不加virtual关键字,也是构成重写的,因为继承后基类的虚函数被继承下来在派生类中依旧保持虚函数属性,我们只是重写了它。

(5)析构函数的重写行为

基类中的析构函数如果是虚函数,那么派生类中的析构函数就重写了基类的析构函数。虽然基类和父类的析构函数名称不同,看起来不符合重写的规则,事实却并非如此,这里可以理解为编译器对析构函数的名称做了特殊处理,编译之后,析构函数的名称统一处理成了destructor,由此也说明基类的析构函数最好也写成虚函数。

若不写成虚函数,未构成多态,则是普通函数的调用,只与类型有关:

class Person
{
public:
	 ~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student :public Person
{
public:
	 ~Student()
	{
		cout << "~Student" << endl;
	}
};
int main()
{
	Person* ps = new Person;
	Person* st = new Student;
	delete ps;
	delete st;
	system("pause");
	return 0;
}

输出结果:

可以看出,st是person类型的指针,虽然指向了子类的构造函数,但是只完成了切片,只拿到了子类中父类的那部分,调了父类的析构函数。此时子类并没有被析构,造成了内存泄露。需要注意的是析构时,底层是这样实现的,先重载了ps->distructor(),然后再free(ps)

写成了虚函数,则构成了多态:

三、接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,就不要把函数定义成虚函数。

四、抽象类

抽象类从字面上可以理解为现实世界没有的,用来描述一些比较泛化的不具体的类别。

在虚函数的后面写上=0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象(也就是派生类继承了基类的纯虚函数),只有重写虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现了接口继承。

下面 一段代码来演示一下抽象类:

class Car
{
public:
	virtual void Drive() = 0;
};
class Person :public Car
{
public:
};

int main()
{
	Person c;
	system("pause");
	return 0;
}

输出结果:

可以看出上述代码执行出错,子类Person继承了父类的纯虚函数,是抽象类,不能实例化出对象,所以子类必须完成虚函数的重写,不然就没有意义。

因此得出一个结论,纯虚函数是一种强制派生类重写虚函数的一种机制。

在实际中,C++11提供override和final来修饰虚函数

首先看一段代码:

class Car
{
public:
	virtual void Drive() = 0;
};
class Person :public Car
{
public:
	void Drive() override
	{
		cout << "舒适" << endl;
	}
};

int main()
{
	Person c;
	c.Drive();
	system("pause");
	return 0;
}

输出结果:

可以看出override修饰派生类虚函数强制完成重写。

下面再介绍一下final关键字,它修饰基类的虚函数不能被派生类重写

class Car
{
public:
	virtual void Drive() final
	{

	}
};
class Person :public Car
{
public:
	void Drive()
	{
		cout << "舒适" << endl;
	}
};

int main()
{
	Person c;
	c.Drive();
	system("pause");
	return 0;
}

输出结果:

以上是关于多态的基础知识点,下篇博客将介绍多态的原理。

猜你喜欢

转载自blog.csdn.net/yam_sunshine/article/details/88778626
今日推荐