一、多态引入
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
根据代码,引入多态的概念:
#include <iostream>
using namespace std;
//定义一个成人类
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); // void Func(Person& p) 参数传参,函数调用
Func(st); // void Func(Person& p) 参数传参,函数调用
return 0;
}
执行结果可以看出根据对象的不同,调用出来不同的结果
多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态 。
修改上面的代码,观察结果:
#include <iostream>
using namespace std;
class Person {
public:
//修改前:virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
void BuyTicket() { cout << "成人买票-全价" << endl; }
};
class Student : public Person {
public:
//修改前:virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
void BuyTicket() { cout << "学生买票-半价" << endl; }
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
我们去掉virtual
关键字之后,看到结果都是调用父类的BuyTicket()
函数。 原因分析:student类继承person类,两个类里面的BuyTicket()
函数函数名一样,构成重定义(1.函数名相同 2.两个函数分别在父子类的作用域里 3. 父子类的同名函数不构成重写),所以子类对象赋值给父类对象的时候,会将子类对象中的父类切出来,自然而然调用的是父类的成员函数
我们再修改代码,思考结果:
#include <iostream>
using namespace std;
class Person {
public:
virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};
void Func(Person p) //修改前:Person& p
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
这个结果的产生原因和上面的原因是一致的,父类赋值给父类对象自然调用父类的成员函数、子类对象赋值给父类对象造成“切片”,将子类中的父类那一部分切出去,让父类对象调用
再修改代码,看结果:
#include <iostream>
using namespace std;
class Person {
public:
virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};
void Func(Person* p) //修改前:Person& p
{
p->BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(&ps); //修改前 Func(ps);
Func(&st); //修改前 Func(st);
return 0;
}
使用指针的时候又根据指向对象的不同,调用出不同的结果
根据上面的代码结果,我们可以大概感知到多态就是根据不同继承关系的类对象,调用同一函数时产生了不同的行为。那么产生多态的条件是什么呢?
文章目录
- 一、多态引入
- 修改上面的代码,观察结果:
- 我们再修改代码,思考结果:
- 再修改代码,看结果:
- 二、多态构成的条件
- 回顾上面的代码,感受多态的条件
- 析构函数的重写-析构函数名统一会被处理成destructor()
- C++11 新语法:override 和 final
- 抽象类
- 1、概念
- 2、 抽象类代码感知
- 总结
二、多态构成的条件
重写的概念
- 两个函数分别在子类和父类的作用域中
- 函数名 / 返回值 / 参数都必须相同
- 两个函数必须是虚函数 (被virtual 修饰的类成员函数成为虚函数)
- 注意:参数只看类型是否相同,不看缺省值
多态构成的条件
- 需要在继承中使用
- 必须使用父类的引用或者指针调用虚函数
- 被调用的函数必须是虚函数,且子类必须对父类的虚函数进行重写
回顾上面的代码,感受多态的条件
#include <iostream>
using namespace std;
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);
return 0;
}
这时候指针p,它指向什么类型,就调用该类型的对象里面的成员
析构函数的重写-析构函数名统一会被处理成destructor()
只有子类 Student 的析构函数重写了 Person 的析构函数,下面的 delete 对象调用析构函
数,才能构成多态,才能保证 p1 和 p2 指向的对象正确的调用析构函数。
函数名处理成destructor() 才能满足多态:
如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,
都与父类的析构函数构成重写,虽然父类与子类析构函数名字不同。虽然函数名不相同,
看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处
理,编译后析构函数的名称统一处理成destructor。
class Person {
public:
virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
virtual ~Student() { cout << "~Student()" << endl; }
};
int main()
{
Person* p1 = new Person;
Person* p2 = new Student;
delete p1;
delete p2;
return 0;
}
所以当父类的指针指向子类的对象时,一定需要将父类的析构函数写成虚函数,如果不定义成虚函数,那么delete p
的时候,就会只父类的析构函数(切片),不会调用子类的析构函数,造成内存泄漏
C++11 新语法:override 和 final
final:修饰虚函数,表示该虚函数不能再被重写
override: 检查子类虚函数是否重写了父类某个虚函数,如果没有重写编译报错
抽象类
1、概念
在虚函数的后面写上 “= 0” ,则这个函数为纯虚函数,包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。子类继承后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。纯虚函数规范了子类必须重写,另外纯虚函数更体现出了接口继承。
2、 抽象类代码感知
#include <iostream>
using namespace std;
class Car
{
public:
virtual void Drive() = 0;
// // 实现没有价值,因为没有对象会调用他
// /*virtual void Drive() = 0
// {
// cout << " Drive()" << endl;
// }*/
};
class Benz :public Car
{
public:
virtual void Drive() //重写父类的纯虚函数
{
cout << "Benz-舒适" << endl;
}
};
class BMW :public Car
{
public:
virtual void Drive() //重写父类的纯虚函数
{
cout << "BMW-操控" << endl;
}
};
void Test()
{
Car* pBenz = new Benz;
pBenz->Drive();
Car* pBMW = new BMW;
pBMW->Drive();
}
int main()
{
Test();
return 0;
}
总结
今天我们熟悉了多态的构成条件以及多态的使用,下一次我们详细聊聊多态的原理,感知一下为什么会出现这样的结果?编译器是怎么做得?