1. 多态的基本概念
多态” 指的是 同一名字的事物可以完成不同的功能。
多态可以分为编译时的多态和运行时的多态。
编译时的多态:主要是指 函数的重载(包括运算符的重载)。对重载函数的调用,在编译时就能根据实参确定应该调用哪个函数,因此叫编译 时的多态;
运行时的多态:和继承、虚函数等概念有关。
有虚函数:基类指针指向基类对象时就使用基类的成员,指向派生类对象时就使用派生类的成员
这种就叫多态
C++ 提供多态的目的是:可以通过基类指针对所有派生类的成员变量和成员函数进行 “全方位” 的访问。
2. 构成多态的条件
- 必须存在继承关系;
- 继承关系中必须有同名的虚函数,并且它们是覆盖关系(函数原型相同)。
- 存在基类的指针,通过该指针调用虚函数。
#include<iostream>
using namespace std;
// Person类
class Person
{
public:
Person(string name, int age) : name(name), age(age)
{
}
virtual void info()
{
cout << "Call Person info, Name = " << this->name << " Age = " << this->age << endl;
}
protected:
string name;
int age;
};
class Student : public Person
{
public:
Student(string name, int age, float score) : Person(name, age), score(score)
{
}
void info()
{
cout << "Call Student info, Name = " << this->name << " Age = " << this->age << " Score = " << score << endl;
}
protected:
float score;
};
int main()
{
Person *person = new Person("zs", 20);
person->info();
person = new Student("ww", 21, 90);
person->info();
return 0;
}
3. 引用实现多态
在 C++ 中,多态的实现,除了可以使用子类的指针指向父类的对象之外,还可以通过引用来实现多态,不过没有指针灵活。
Person p1("地灵殿", 18);
Student p2("恋恋", 18,99);
Person &rPerson = p1;
Person &sPerson = p2;
rPerson.info();
sPerson.info();
4. 虚函数
使用 virtual 关键字修饰的函数 被称为虚函数。有虚函数才能实现多态。
什么时候声明虚函数:
其一、成员函数的类会作为基类
其二、成员函数希望在被继承后更改功能
语法:
virtual type funcName(plist){}
#include<iostream>
using namespace std;
class Animal{
public:
virtual void eat(){
cout << "我是animal" << endl;
}
};
class Dog:public Animal{
public:
void eat()
{
cout << "看是狗狗吃饭" << endl;
}
};
class Cat:public Animal{
public:
void eat()
{
cout << "看是猫猫吃饭" << endl;
}
};
int main()
{
Animal *pa = new Animal();
pa->eat();
pa = new Dog();
pa->eat();
pa = new Cat();
pa->eat();
return 0;
}
5. 虚析构
c++的构造函数不能是虚函数,但析构函数可以被声明为虚构函数,有时候还必须被声明为虚构函数。用来做基类的类的析构函数一般都是虚函数。
语法:
virtual ~FuncName() {}
#include<iostream>
using namespace std;
class Animal{
public:
virtual void eat(){
cout << "我是animal" << endl;
}
virtual ~Animal() { cout << "我死啦" << endl; }
};
class Dog:public Animal{
public:
void eat()
{
cout << "看是狗狗吃饭" << endl;
}
~Dog() { cout << "狗狗我死啦" << endl; }
};
class Cat:public Animal{
public:
void eat()
{
cout << "看是猫猫吃饭" << endl;
}
~Cat() { cout << "猫猫也没了" << endl; }
};
int main()
{
Animal *pa = new Animal();
pa->eat();
pa = new Dog();
pa->eat();
pa = new Cat();
pa->eat();
delete pa;
return 0;
}
6. 虚函数表
虚函数是通过虚函数表来实现的,类包含虚函数表,对象包含虚指针,派生类会生成一个兼容基类的虚函数表。虚函数表中存储的是虚函数的地址。虚函数表在代码编译阶段就生成了。
7. C++如何实现动态绑定
当编译器发现类中有虚函数的时候,编译器会创建一张虚函数表,把虚函数的函数入口地址放到虚函数表中, 并且在类中秘密增加一个指针,这个指针就是vpointer(缩写vptr), 这个指针是指向对象的虚函数表。在多态调用的时候,根据vptr指 针,找到虚函数表来实现动态绑定。
在对象构建的时候,vptr才指向虚函数表。子类继承基类时,子类会继承基类的vptr指针,这个vptr指针指向的是基类虚函数表,只有当子类创建对象时,之类的vptr指针指向子类的虚函数表。
8. 抽象基类和纯虚函数
在类中加入一个纯虚函数就会变成抽象基类。纯虚函数在函数前加virtual在后面加上=0;
当继承一个抽象类时,必须实现所有的纯虚函数,否则继承出的类也将是一个抽象类。
语法:
纯虚函数:virtual type funcName(plist) = 0