今天来学习一个新的概念,多态!!!多态在C++编程中具有重要的地位与意义,是面向对象的一个重要思想!
加qq1126137994一起学习更多技术~
1、问题引入
父类与子类之间具有赋值兼容性;
*子类对象可以当做父类对象使用(赋值兼容性)
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
看一个例子程序来理解一下:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
int mi;
void add(int i)
{
mi += i;
}
void add(int a, int b)
{
mi += (a + b);
}
};
class Child : public Parent
{
public:
int mv;
void add(int x, int y, int z)
{
mv += (x + y + z);
}
};
int main()
{
Parent p;
Child c;
p = c;
Parent p1(c);
Parent& rp = c;
Parent* pp = &c;
rp.mi = 100;
rp.add(5); // 没有发生同名覆盖?
rp.add(10, 10); // 没有发生同名覆盖?
/* 为什么编译不过? */
// pp->mv = 1000; // pp是父类指针,指向子类c,那么c对象就退化为父类对象
// pp->add(1, 10, 100); // pp只能访问父类对象,并且可以访问父类与子类同名的对象
// 而不用指定作用域。
return 0;
}
-当使用父类指针(引用)指向子类对象时
- 子类对象退化为父类对象
- 只能访问父类中定义的成员
- 可以直接访问被子类覆盖的同名成员而不用指定作用域
2、函数重写
- 子类中可以重定义父类中已经存在的成员函数
- 这种重定义发生在继承中,叫做函数重写
- 函数重写是函数同名覆盖的特殊情况
那么当函数重写遇上赋值兼容会发生什么?
先来看一个简单的例子:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
int mi;
void add(int i)
{
mi += i;
}
void add(int a, int b)
{
mi += (a + b);
}
void print()
{
cout << "I'm Parent." << endl;
}
};
class Child : public Parent
{
public:
int mv;
void add(int x, int y, int z)
{
mv += (x + y + z);
}
void print()
{
cout << "I'm Child." << endl;
}
};
void how_to_print(Parent* p)
{
p->print();
}
int main()
{
Parent p;
Child c;
how_to_print(&p); // Expected to print: I'm Parent.
how_to_print(&c); // Expected to print: I'm Child.
return 0;
}
运行结果为:
I’m Parent.
I’m Parent.
很显然,这个结果并不是我们想要的,我们想要的是执行 how_to_print(&c); 后打印I’m Child.
这就是函数print重写后,遇到赋值兼容的情况。
问题分析:
- 编译期,间编译器只能根据指针的类型判断所指向的对象
- 根据赋值兼容,编译器认为父类指针指向的是父类对象
- 因此,编译结果只可能是调用父类中定义的同名的函数
小结:
- 子类对象可以当做父类对象使用(赋值兼容)
- 父类指针可以正确的指向子类对象
- 父类引用可以正确的代表子类对象
- 子类中可以重写父类的成员函数
3、解决办法-多态的概念
- 父类中被重写的函数依然会继承给子类
- 子类中被重写的函数会覆盖父类中的同名函数
而面向对象期望的行为是:
- 根据实际的对象类型调用具体的成员重写函数
- 父类指针(引用)指向
*父类对象,则调用父类中定义的函数
*子类对象,则调用子类中定义的重写函数
要实现这个行为,就需要引出面向对象中的多态的概念:
面向对象中多态的概念:
- 根据实际的对象类型,决定函数调用的具体目标
- 通用的调用语句,在实际的运行中有多种不同的表现形态
C++语言直接支持多态的概念
- 通过使用virtual关键字对多态进行支持
- 被virtual声明的函数被重写后具有多态性
- 被virtual声明的函数叫做虚函数
修改上一个程序:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
int mi;
virtual void print()
{
cout << "I'm Parent." << endl;
}
};
class Child : public Parent
{
public:
int mv;
void print()
{
cout << "I'm Child." << endl;
}
};
void how_to_print(Parent* p)
{
p->print();
}
int main()
{
Parent p;
Child c;
how_to_print(&p);
how_to_print(&c);
return 0;
}
运行结果:
I’m Parent.
I’m Child.
可以看出,这是我们想要的结果。
多态的意义:
- 在程序运行过程中展现出动态特性
- 函数重写必须多态实现,否则没有意义
- 多态是面向对象组件化程序设计的基础特性
4、多态在理论中的概念与意义
理论中的概念:
- 静态联编
*在函数编译期间就能确定具体的函数调用
-如.函数重载 - 动态联编
*在程序实际运行后才能确定函数的具体调用
-如.函数重写
给个例子说明:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
virtual void func()
{
cout << "void func()" << endl;
}
virtual void func(int i)
{
cout << "void func(int i) : " << i << endl;
}
virtual void func(int i, int j)
{
cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
}
};
class Child : public Parent
{
public:
void func(int i, int j)
{
cout << "void func(int i, int j) : " << i + j << endl;
}
void func(int i, int j, int k)
{
cout << "void func(int i, int j, int k) : " << i + j + k << endl;
}
};
void run(Parent* p)
{
p->func(1, 2); // 展现多态的特性
// 动态联编
}
int main()
{
Parent p;
p.func(); // 静态联编
p.func(1); // 静态联编
p.func(1, 2); // 静态联编
cout << endl;
Child c;
c.func(1, 2); // 静态联编
cout << endl;
run(&p);
run(&c);
return 0;
}
上述程序的运行结果为:
void func()
void func(int i) : 1
void func(int i, int j) : (1, 2)
void func(int i, int j) : 3
void func(int i, int j) : (1, 2)
void func(int i, int j) : 3
5、拓展训练
下面是一个展现多态,继承的程序,参考教学视频第49课第二个视频!
#include <iostream>
#include <string>
using namespace std;
class Boss
{
public:
int fight()
{
int ret = 10;
cout << "Boss::fight() : " << ret << endl;
return ret;
}
};
class Master
{
public:
virtual int eightSwordKill()
{
int ret = 8;
cout << "Master::eightSwordKill() : " << ret << endl;
return ret;
}
};
class NewMaster : public Master
{
public:
int eightSwordKill()
{
int ret = Master::eightSwordKill() * 2;
cout << "NewMaster::eightSwordKill() : " << ret << endl;
return ret;
}
};
void field_pk(Master* master, Boss* boss)
{
int k = master->eightSwordKill();
int b = boss->fight();
if( k < b )
{
cout << "Master is killed..." << endl;
}
else
{
cout << "Boss is killed..." << endl;
}
}
int main()
{
Master master;
Boss boss;
cout << "Master vs Boss" << endl;
field_pk(&master, &boss);
cout << "NewMaster vs Boss" << endl;
NewMaster newMaster;
field_pk(&newMaster, &boss);
return 0;
}
运行结果为:
Master vs Boss
Master::eightSwordKill() : 8
Boss::fight() : 10
Master is killed…
NewMaster vs Boss
Master::eightSwordKill() : 8
NewMaster::eightSwordKill() : 16
Boss::fight() : 10
Boss is killed…
6、总结
- 函数重写只可能发生在父类与子类之间
- 根据实际对象的类型调用具体的函数,多态性
- virtual关键字是C++中支持多态的唯一方法
- 被重写的虚函数可以表现出多态性
想获得各种学习资源以及交流学习的加我:
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。