概念
多态是面向对象编程中的一个重要概念,它允许不同类型的对象对同一个消息做出不同的响应。具体的来说,当相同的消息传递给不同的对象时,这些对象能够以不同的方式进行处理,从而产生不同的行为。
对于多态的实现,需要一定的条件
虚函数的重写
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person p;Student s;Func(p);Func(s);return 0;
}
但有两个例外。
协变
class A {};
class B : public A {};
class Person {
public:virtual A* f() { cout << "A:f()" << endl;return new A;}
};
class Student : public Person {
public:virtual B* f(){cout << "B:f()" << endl;return new B;}
};int main()
{Person* p = new Student;p->f();return 0;
}
析构函数的重写
class Person {
public:virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;//p1->destructor()+ operator delete(p1)delete p2;//p2->destructor()+ operator delete(p2)return 0;
}
c++11的两个关键字
多态的实现
class Person {
public:virtual ~Person(){ cout << "~Person()" << endl;}virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual ~Student(){ // delete _ptr;cout << "~Student()" << endl;}virtual void BuyTicket() { cout << "买票-半价" << endl; }
};int main()
{Person p;Student s;//多态调用Person* p1 = new Person;Person* p2 = new Student;p1->BuyTicket();p2->BuyTicket();delete p1;delete p2;return 0;
}
多态的原理
虚函数表
class Base
{
public:virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};void f(Base* ptr)
{ptr->Func1();
}// vitual function table
int main()
{Base bb;Derive dd;f(&bb);f(&dd);return 0;
}
总结:
1.派生类对象d也有一个虚表指针,并且该虚表指针地址和基类的不相同;
2.在虚表中,发现func1的地址不一样,而func2的地址一样,这是因为func1完成了重写,被重写的虚函数地址是不一样的;
3.在虚表中,只有是虚函数才有被放进虚表中;
4.虚表本质就是一个存虚函数指针的指针数组,一般情况这个数组最后面会放nullptr;
5.对于派生类虚表的生成,派生类会先拷贝一份基类的虚表地址(地址是不一样的),如果派生类中有函数进行了重写,那么用派生类自己的虚函数覆盖基类的虚函数,最后派生类有新增的虚函数按其在派生类中的声明次序增加到派生类虚表的最后;
原理
所以可以看出,多态实际上就是利用了虚函数的重写,让虚函数的地址是不同的,当基类的指针或引用指向自己或者派生类时,会根据虚表中对应的虚函数地址来进行运行;
这就达到了不同对象完成同一行为展示出的不同形态;
并且满足多态的函数调用,不是在编译时确定的,是在运行后到具体对象中取栈的,普通的函数调用都是在编译期间确定好的。
动态绑定和静态绑定
- 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
- 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。
抽象类
class Car
{
public:virtual void Drive() = 0;
};
class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl;}
};
class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};int main()
{//抽象类不能实例化//Car c1;Benz b;Car* p = new Benz;p->Drive();p = new BMW;p->Drive();
}
接口继承和实现继承
虚函数表存于哪个区域
class Base
{
public:Base():_b(2){cout << "Base()" << endl;}virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}virtual void Func3(){cout << "Derive::Func3()" << endl;}
private:int _d = 2;
};int main()
{Base b;Base b1;Base b2;Derive d;int i = 0;static int j = 1;int* p1 = new int;const char* p2 = "xxx";printf("栈:%p\n", &i);printf("静态区:%p\n", &j);printf("堆:%p\n", p1);printf("常量区:%p\n", p2);Base* p3 = &b;Derive* p4 = &d;printf("Base虚表地址:%p\n", *(int*)p3);printf("Base虚表地址:%p\n", *(int*)p4);return 0;
}
单继承的虚表
typedef void(*VF_PTR)();//函数指针
void PrintVFT(VF_PTR* vft,int n)
{for (size_t i = 0; i<n; i++){printf("[%d]:%p->", i, vft[i]);VF_PTR f = vft[i];(*f)();}cout << endl << endl;
}//打印虚函数表
int main()
{Derive d;PrintVFT((VF_PTR*)(*((int*)&d)));
}
多继承的虚表
class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1;
};class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2;
};class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};int main()
{Derive d;cout << sizeof(d) << endl;Base1* ptr1 = &d;Base2* ptr2 = &d;PrintVFT((VF_PTR*)(*(int*)ptr1),3);PrintVFT((VF_PTR*)(*(int*)ptr2),2);return 0;
}