住房城乡建设厅网站,wordpress又拍云插件,柴沟堡做网站,wordpress 文章发布时间多态一、多态的概念及定义1.1 虚函数1.2 虚函数重写的特殊情况1.3 override 和 final二、抽象类2.1 概念2.2 用处三、多态的原理3.1 虚函数表3.1.1 虚函数与虚表的位置3.2 多态的原理3.3 静态绑定和动态绑定四、单/多继承的虚函数表4.1 单继承的虚函数表4.2 多继承的虚函数表一…
多态一、多态的概念及定义1.1 虚函数1.2 虚函数重写的特殊情况1.3 override 和 final二、抽象类2.1 概念2.2 用处三、多态的原理3.1 虚函数表3.1.1 虚函数与虚表的位置3.2 多态的原理3.3 静态绑定和动态绑定四、单/多继承的虚函数表4.1 单继承的虚函数表4.2 多继承的虚函数表一、多态的概念及定义
多态 去完成某个行为当不同的对象去完成时会产生出不同的状态。 构成多态有两个条件 1️⃣ 必须是虚函数的重写。 2️⃣ 必须通过父类的指针或者引用调用虚函数 隐藏/重定义的条件函数名相同 重写/覆盖的条件函数名、返回值、参数都相同且是虚函数
1.1 虚函数
虚函数即被virtual修饰的类成员函数称为虚函数
class A
{
public:// 虚函数virtual void Print(){cout A endl;}
};class B : public A
{
public:// 虚函数的重写/覆盖virtual void Print(){cout B endl;}
};int main()
{A a;B b;A* pa a;A* pb b;pa-Print();pb-Print();return 0;
}结果 A B 结论 普通调用跟的调用的对象的类型有关 多态调用跟指针/引用指向的对象有关
1.2 虚函数重写的特殊情况
1️⃣ 子类的虚函数可以不加virtual 因为子类对象会对父类继承下来的虚函数进行重写。 1️⃣ 协变 返回值可以不同但必须是父子类关系的指针或引用父亲就用父类对象子类就用子类对象。 3️⃣ 析构函数
class A
{
public:~A(){cout ~A() _a endl;delete []_a;}int* _a new int[20];
};class B : public A
{
public:~B(){cout ~B() _b endl;delete[]_b;}int* _b new int[20];
};int main()
{A* a new A;A* b new B;delete a;delete b;return 0;
}结果 ~A() 015E8820 ~A() 015E9738 可以看到发生了内存泄漏delete有两个操作1、使用指针调用析构函数。2、operator delete() 而因为析构函数没有用virtual所以是普通调用只与类型有关全部调用的是A的析构函数。
所以建议析构函数也加上virtual。
1.3 override 和 final
final关键字在父类修饰虚函数表示该虚函数不能再被重写
class A
{
public:// 虚函数virtual void Print() final{cout A endl;}
};class B : public A
{
public:// 不能被重写virtual void Print(){cout B endl;}
};
override在子类修饰虚函数检查子类是否重写如果没有重写则编译报错
class A
{
public:// 虚函数virtual void Print(){cout A endl;}
};class B : public A
{
public:// 检查是否被重写virtual void Print() override{cout B endl;}
};二、抽象类
2.1 概念 在虚函数的后面写上 0 则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类也叫接口类抽象类不能实例化出对象。 派生类继承后也不能实例化出对象只有重写纯虚函数派生类才能实例化出对象。纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承。 class A
{
public:// 纯虚函数virtual void Print() 0;
};class B : public A
{
public:virtual void Print(){cout B endl;}
};int main()
{A a; // yesB b; // noreturn 0;
}2.2 用处
当不能定义出具体的类的时候就可以用抽象类比如说车的品牌酒的不同种类。 或者当想要强制重写虚函数的时候也可以使用。
三、多态的原理
3.1 虚函数表
class A
{
public:virtual void Print() {}int _a;
};int main()
{A a;cout sizeof(a) endl;return 0;
}结果 8 出现这种结果的原因是因为有__vfptr虚表指针。虚表指针放的是虚函数的地址。
class A
{
public:virtual void Print1() {}virtual void Print2() {}void Print3() {}int _a;
};int main()
{A a;cout sizeof(a) endl;return 0;
}结果依然不变是8。 Print1和Print2会放进虚函数表中
class A
{
public:virtual void Print1() {cout A::Print1() endl;}virtual void Print2(){cout A::Print2() endl;}void Print3(){cout A::Print3() endl;}int _a 0;
};class B : public A
{
public:virtual void Print1(){cout B::Print1() endl;}int _b 0;
};int main()
{A a;B b;return 0;
}可以看出子类先拷贝了父类的虚表完成重写的虚函数虚表对应的位置覆盖成重写的虚函数。
3.1.1 虚函数与虚表的位置
注意 虚表存的是虚函数指针不是虚函数虚函数和普通函数一样的都是存在代码段的只是他的指针又存到了虚表中。另外对象中存的不是虚表存的是虚表指针。而虚表存的位置也是代码段。 3.2 多态的原理
为什么能做到指向父类对象的指针调用的是父类函数指向子类就调用子类函数。 原因是运行时会找虚函数表虚函数表就是一个函数指针来找到对应的虚函数这种操作叫做动态绑定。
3.3 静态绑定和动态绑定 静态绑定又称为前期绑定(早绑定)在程序编译期间确定了程序的行为也称为静态多态 比如函数重载 动态绑定又称后期绑定(晚绑定)是在程序运行期间根据具体拿到的类型确定程序的具体 行为调用具体的函数也称为动态多态。 四、单/多继承的虚函数表
4.1 单继承的虚函数表
class A
{
public:virtual void Print1() {cout A::Print1() endl;}virtual void Print2(){cout A::Print2() endl;}int _a 0;
};class B : public A
{
public:virtual void Print1(){cout B::Print1() endl;}virtual void Print3(){cout B::Print3() endl;}void Print4(){cout B::Print4() endl;}int _b 0;
};typedef void(*VfPtr)();void VfPrint(VfPtr vft[])// 打印虚表
{for (int i 0; vft[i]; i){printf([%d]:%p-, i, vft[i]);vft[i]();}cout endl;
}int main()
{A a;B b;VfPrint((VfPtr*)(*(int*)a));VfPrint((VfPtr*)(*(int*)b));return 0;
}
结果 可以看出子类如果有自己的虚函数会放到虚表的最后。
4.2 多继承的虚函数表
class A
{
public:virtual void Print1() {cout A::Print1() endl;}virtual void Print2(){cout A::Print2() endl;}int _a 0;
};class B : public A
{
public:virtual void Print1(){cout B::Print1() endl;}virtual void Print2(){cout B::Print2() endl;}int _b 0;
};class C : public A, public B
{
public:virtual void Print1(){cout C::Print1() endl;}virtual void Print3(){cout C::Print3() endl;}int c 0;
};typedef void(*VfPtr)();void VfPrint(VfPtr vft[])// 打印虚表
{for (int i 0; vft[i]; i){printf([%d]:%p-, i, vft[i]);vft[i]();}cout endl;
}int main()
{A a;B b;C c;VfPrint((VfPtr*)(*(int*)a));VfPrint((VfPtr*)(*(int*)b));// 第一张虚表VfPrint((VfPtr*)(*(int*)c));// 第二张虚表VfPrint((VfPtr*)(*(int*)((char*)c sizeof(A))));return 0;
}
对象c有两张虚表(从a和b中继承下来的)。注意多继承不一定有多张虚表因为有可能有的类没有虚函数。 可以看出多继承中子类虚函数会加到第一张虚表中。