1.什么是多态?
多态简单的讲就是“一个接口,多种方法”,程序在运行时才决定调用的函数,他是面向对象的核心概念。当多态应用形参的数据交,可以接受更多的类型,当多态用于返回值类型的时候,可以返回更多类型的数据,多态可以让你的代码拥有更好的扩展性
构成多态的条件
- 继承的存在
- 子类要重写父类的虚函数
- 父类的指针/引用调用重写虚函数
什么是重写:
- 不在同一作用域内(分别在父类和子类中)
- 基类函数必须是虚函数(virtual关键字)
- 函数名相同,参数相同,返回值相同(协变除外)
- 访问修饰符可以不同(公有,保护…)
场景一:父类和子类都加virtual:
class person
{
public:
virtual void buytickets()
{
cout << "买票" << endl;
}
};
class student :public person
{
public:
virtual void buytickets()
{
cout << "买票——半价" << endl;
}
};
void Fun(person& p)
{
p.buytickets();
}
void Test()
{
person p;//父类对象
student s;//子类对象
Fun(p);
Fun(s);
}
输出为:
当使用基类的指针或引用调用重写的虚函数时,当指向父类对象调用的就是父类的虚函数,指向子类的对象就是调用子类的虚函数。
多态跟类型无关,跟对象有关
场景二:基类中函数不加virtual
构成多态时,父类的函数必须是虚函数(virtual),为什么
class person
{
public:
void buytickets()
{
cout << "买票" << endl;
}
};
class student :public person
{
public:
virtual void buytickets()
{
cout << "买票——半价" << endl;
}
};
void Fun(person& p)
{
p.buytickets();
}
void Test()
{
person p;//父类对象
student s;//子类对象
Fun(p);
Fun(s);
}
- 输出为:
- 如果将父类的virtual,那么就不能构成多态,即使你创建了子类对象和父类对象,调用的也都是父类的函数//输出“买票”。与对象无关,不能构成多态
- 而如果将子类中的virtual去掉,而父类中的还在,同样可以构成多态。
场景三:派生类中函数不加virtual
class person
{
public:
virtual void buytickets()
{
cout << "买票" << endl;
}
};
class student :public person
{
public:
void buytickets()
{
cout << "买票——半价" << endl;
}
};
void Fun(person& p)
{
p.buytickets();
}
void Test()
{
person p;//父类对象
student s;//子类对象
Fun(p);
Fun(s);
}
输出为:
2.多态的分类:
- 编译时的多态性(静态多态):静态多态就是重载,因为是在编译时即确定类型大小,所以称为静态多态。
- 运行时多态性(多态多态):通过继承重写基类的虚函数实现,在程序运行时决议确定,所以称为动态
动态多态:上述代码即使动态多态
每一个带有虚函数的对象都会有一个虚函数表,虚函数表里存的时函数指针,调用的时候,指针就去虚函数表里查找。
发生重写之后,下一次父类指向我们调用的fun()函数时,它调用到的就是子类的fun()函数。
多态:
- 多态是面向对象的三大特征直以,分为静态多态和多态多态
- 静态多态包含函数重载与泛型编程,静态多态是程序调用函数,编译器决定使用那块可执行代码块
- 动态多态是由继承机制及虚函数实现的,通过指向派生类的基类指针或引用,访问派生类中透明覆盖成员函数
- 多态的作用:把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,一适应需求的不断变化。
类成员函数中重载/重定义(隐藏)/重写(覆盖)的区别
1.函数重载:通常是一组功能相似但是参数类型不同的函数
- 函数要在相同的作用域中
- 函数名相同
- 函数的参数列表不同(参数个数,参数顺序,参数类型),与返回值无关
2.函数覆盖(重写):覆盖是指在派生类里覆盖基类的函数
- 函数在不同作用域中(基类和派生类)
- 俩个函数名相同/参数相同/返回值相同(协变除外)
- 基类必须是虚函数(派生类最好也是)
- 访问修饰符可以不同
3.函数隐藏(重定义):派生类的函数屏蔽了与其名字相同的基类函数
- 函数的作用域不同(基类,派生类)
- 函数名相同(只要名字相同就可以构成隐藏)
- 如果派生类函数与基类函数参数相同,但是基类中没有virtual,就构成函数隐藏