C++是一门面向对象的语言,其三大特性,封装、继承、多态。
本文将介绍来好好讲讲多态…
多态的概念:
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
虚函数: 即被 virtual 修饰的类成员函数称为虚函数。
class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
虚函数的重写(覆盖):
下面我们实现一个简单的买票系统 —— 要求实现不同的对象做同一件事产生不同的状态:
//多态只用的样例:
class Person
{
public:Person(const char* name):_name(name){}//虚函数virtual void BuyTicket() { cout << _name << " Person: 买票-全价 100¥" << endl; }protected:string _name;//int _id;
};class Student : public Person
{
public:Student(const char* name):Person(name){}//虚函数 + 函数名/参数/返回值 -> 重写/覆盖virtual void BuyTicket() { cout << _name << " Student: 买票-半价 50¥" << endl; }
};class Soldier : public Person
{
public:Soldier(const char* name):Person(name){}//虚函数 + 函数名/参数/返回值 -> 重写/覆盖virtual void BuyTicket() { cout << _name << " Soldier: 优先买预留票-88折 100¥" << endl; }
};void Pay(Person* ptr)
{ptr->BuyTicket();delete ptr;
}//赋值兼容的转换,父类指针可以指向父类对象,也可以指向子类对象
void Pay(Person& ptr)
{ptr.BuyTicket();
}全部都去调用父类去了 -- 不构成多态
//void Pay(Person ptr)
//{
// ptr.BuyTicket();
//}int main()
{int option = 0;cout << "=========================================" << endl;do{cout << "请选择身份:";cout << "1、普通人 2、学生 3、军人" << endl;cin >> option;cout << "请输入名字:";string name;cin >> name;//switch case语句里面,是不能支持定义对象的,要加一个域{}//加完域之后就是局部域了switch (option){case 1:{Person p(name.c_str());Pay(p);break;}case 2:{Student s(name.c_str());Pay(s);break;}case 3:{Soldier s(name.c_str());Pay(s);break;}default:cout << "输入错误,请从新输入" << endl;break;}cout << "=========================================" << endl;} while (option != -1);return 0;
}
上述代码总结:

1.为什么一定是父类的指针和引用去调用虚函数,父类的对象可以吗?

先看运行结果:

2.如果有一个条件不满足多态的话(当参数不满足相同时):

我们来看运行结果:

1.协变的概念:
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
2.父子关系的指针:
class A
{};class B : public A
{};class Person
{
public:virtual A* f(){cout << "virtual A* Person::f()" << endl;return nullptr;}
};class Student : public Person
{
public:virtual B* f(){cout << "virtual B* Student::f()" << endl;return nullptr;}
};int main()
{Person p;Student s;Person* ptr = &p;ptr->f();ptr = &s;ptr->f();return 0;
}

3.父子关系的引用:


4.父子关系的对象:


可见父子关系的对象是不行的。
C++在这里搞麻烦了,意义不是很大。
5.如果同为父类:


这里则是继承中的隐藏。
补充:
建议:
1.以下程序输出结果是什么?
class A
{
public:virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }virtual void test() { func(); }
};class B : public A
{
public:void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};int main()
{B* p = new B;p->test();return 0;
}
运行结果:

重写其实是一种接口继承。

2.看下面代码的运行结果是什么?

运行结果:

这里就是类成员的普通调用,并不是多态的调用。
3.再来看一组:

这才是多态的调用,时刻记住多态的两个条件!
多态:拿一个类的指针去调用另一个类的函数。
1.析构函数不构成多态的情况:


普通调用看的指针类型,指针类型是Person * ,就去调用Person的析构函数去了
问题:
2.解决办法 - 多态:
class Person
{
public:virtual ~Person() { cout << "~Person()" << endl; }
};class Student : public Person
{
public:virtual ~Student() { cout << "~Student()" << endl; }
};int main()
{Person p;Student s;return 0;
}
(1)对于普通对象没有影响:

- 子类对象析构的时候是先子后父,初始化是先父后子
- 调用子类的析构函数会自动调用父类的析构函数
析构函数默认是隐藏关系 – 都被处理成destructor
(2)但是指针的切片调用就会有不同,触发了多态:


建议: