目录
一、定义及实现
多态就是执行某项行为,不同的对象去执行会有不同的状态。之前的函数重载(静态多态)就是一种多态。
1.1 多态条件
1. 必须通过基类的指针或者引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
1.2 虚函数
在函数前加上virtual关键字,该函数就是虚函数。虚函数只能是类成员非静态函数,内联函数不能是虚函数。
class Person
{
virtual void printPrice()
{
cout<<20<<endl;
}
};
1.3 虚函数重写
子类中有一个成员函数与父类中的完全相同(除内部实现外,其余完全相同)。
class Person
{
public:
virtual void printPrice()
{
cout << 20 << endl;
}
};
class Children : public Person
{
public:
virtual void printPrice()
{
cout << 10 << endl;
}
};
int main()
{
Person p;
Children c;
Person& t1 = p;
Person& t2 = c;
p.printPrice();
c.printPrice();
return 0;
}
但是也有两个特别的重写,分别是协变和析构函数重写。
1.3.1协变
协变情况下,父子类中的虚函数成员返回值不同,基类虚函数返回基类对象的指针或者引
用,派生类虚函数返回派生类对象的指针或者引用。
class A {};
class B : public A {};
class Person
{
public:
virtual A& printPrice(A& a)
{
cout << 20 << endl;
return a;
}
};
class Children : public Person
{
public:
virtual B& printPrice(B& b)
{
cout << 10 << endl;
return b;
}
};
1.3.2 析构函数重写
父子类的析构函数名不同,但是编译器会自动在编译时将函数名弄成一样的函数名。析构函数重载利用在delete调用时。
class Person
{
public:
virtual ~Person()
{
cout << "Person 析构" << endl;
}
};
class Children : public Person
{
public:
virtual ~Children()
{
cout << "Children 析构" << endl;
}
};
int main()
{
Person* p1 = new Person;
Person* p2 = new Children;
delete p1;
cout << endl;
delete p2;
return 0;
}
1.4 final
final关键字是c++11语法,在类后面加表示类不能被继承,在虚函数后面加表示不能被重写。
class A final
{};
class B
{
public:
void f() final {}
};
1.5 override
检查子类虚函数是否完成重写。
class A
{
public:
void f() final {}
};
class B : public A
{
public:
void f() override {}
};
二、抽象类
含有纯虚函数的类就叫做抽象类。
2.1 纯虚函数
纯虚函数只声明,不实现(实现没有价值)。
class A
{
virtual void f()=0;
};
子类继承父类后也不能实例化出对象,只有重写纯虚函数,子类才能实例化出对象。纯
虚函数规范了子类必须重写,同时也更能体现接口继承。
class A
{
public:
virtual void f()=0;
};
class B : public A
{
public:
virtual void f()
{
cout<<"B::f()"<<endl;
}
};
三、多态原理
调试一下以下代码,可以发现父子两类中存在一个成员叫做_vfptr的变量存放着一个地址,这个_vfptr叫做虚函数表指针,该表上存放着对应类中的虚函数的地址。
class A
{
public:
virtual void f()
{
cout << "A::f()" << endl;
}
};
class B : public A
{
public:
virtual void f()
{
cout << "B::f()" << endl;
}
};
void test(A& c)
{
c.f();
}
int main()
{
A a;
B b;
test(a);
test(b);
return 0;
}
通过对应类的引用去找该对象类中的虚表,再从虚表中的虚函数地址来调用相应的虚函数,以此来实现多态。
四、多继承中的虚表
多继承会有多张虚表。继承几个类就有几个虚表。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class A
{
public:
virtual void f1()
{
cout << "A::f1()" << endl;
}
virtual void f2()
{
cout << "A::f2()" << endl;
}
};
class B
{
public:
virtual void f1()
{
cout << "B::f1()" << endl;
}
virtual void f3()
{
cout << "B::f3()" << endl;
}
};
class C : public A ,public B
{
public:
virtual void f1()
{
cout << "C::f1()" << endl;
}
};
typedef void(* VF_PTR )();
void printVFTable(VF_PTR* table)
{
for (int i = 0; table[i]!=nullptr; i++)
{
printf("vft[%d]:%p->", i, table[i]);
table[i]();
}
}
int main()
{
C c;
printVFTable((VF_PTR*)(*(void**)&c));
B& b = c;
printVFTable((VF_PTR*)(*(int*)&b));
return 0;
}