网站开发一个月安溪网站开发

当前位置: 首页 > news >正文

网站开发一个月,安溪网站开发,网站开发流程详解,淮安建设机械网站制作文章目录 多态基本概念和原理虚函数的基本原理和概念虚析构和纯虚析构多重继承中的虚函数小结 多态基本概念和原理 多态的基本概念 多态是C面向对象三大特性之一 多态的定义 多态是一种面向对象编程概念#xff0c;指同一个行为#xff08;方法#xff09;在不同的对象上… 文章目录 多态基本概念和原理虚函数的基本原理和概念虚析构和纯虚析构多重继承中的虚函数小结 多态基本概念和原理 多态的基本概念 多态是C面向对象三大特性之一 多态的定义 多态是一种面向对象编程概念指同一个行为方法在不同的对象上有不同的实现方式。其定义可以简单地理解为“同名多形”即同一个方法名可以被不同的类实现并且表现出不同的行为。 多态使得程序具有更高的灵活性和可扩展性因为它使得代码与具体对象的实现细节分离开来从而可以方便地添加新的类或修改旧的类的实现方式同时也使得代码更易于维护。 虚函数和多态的关系 虚函数是实现多态的关键。在基类中声明一个虚函数当子类重写该函数时调用该函数将根据对象类型动态确定。这样做可以实现运行时多态即在程序运行期间根据对象类型动态确定哪个函数应该被调用。这使得代码更加灵活和可扩展并且减少了大量的条件判断语句。因此虚函数和多态密不可分。 多态分为两类 静态多态: 函数重载 和 运算符重载属于静态多态复用函数名动态多态: 派生类和虚函数实现运行时多态 静态多态和动态多态区别 静态多态的函数地址早绑定  -  编译阶段确定函数地址动态多态的函数地址晚绑定  -  运行阶段确定函数地址     动态多态满足条件     1、有继承关系     2、子类重写父类的虚函数     动态多态的使用     父类的指针或者引用指向子类的对象    重写函数返回值类型  函数名 参数列表 完全一致称为重写 #include iostream using namespace std;class Animal{public://speak函数就是虚函数//函数前面加上virtual关键字变成虚函数那么编译器在编译的时候就不能确定函数调用了。virtual void speak() { //类Animal的成员函数speak前不加virtual其size是1加virtual其size是4增加一个虚函数指针cout 动物在说话 endl;}};class Cat :public Animal {public://子类重写父类的虚函数void前加不加virtual均可//重写函数返回值类型 函数名 参数列表 完全一致称为重写void speak() {cout 小猫在说话 endl;}};//执行说话的函数//地址早绑定在编译阶段确定函数地址父类的speak函数前不加virtual,则即使DoSpeak传入参数cat则也是调用的父类的speak函数//如果想让小猫说话那么这个函数地址就不能早绑定需要在运行阶段进行绑定地址晚绑定//我们希望传入什么对象那么就调用什么对象的函数//如果函数地址在编译阶段就能确定那么静态联编//如果函数地址在运行阶段才能确定就是动态联编//动态多态满足条件//1、有继承关系//2、子类重写父类的虚函数//动态多态的使用//父类的指针或者引用指向子类的对象void DoSpeak(Animal animal) //父类的指针或者引用指向子类的对象 Animal animal cat;{animal.speak();}void test() {Cat cat;DoSpeak(cat);} int main(){test();return 0; } 多态的基本原理 实现原理虚函数表虚表指针 编译器处理虚函数的方法是为每个类对象添加一个隐藏成员隐藏成员中保存了一个指向函数地址数组的指针称为虚表指针vptr这种数组成为虚函数表virtual function table, vtbl即每个类使用一个虚函数表每个类对象用一个虚表指针。 举个例子基类对象包含一个虚表指针指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针指向派生类虚函数表。看下面两种情况 如果派生类重写了基类的虚方法该派生类虚函数表将保存重写的虚函数的地址而不是基类的虚函数地址。 如果基类中的虚方法没有在派生类中重写那么派生类将继承基类中的虚方法而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意如果派生类中定义了新的虚方法则该虚函数的地址也将被添加到派生类虚函数表中。 调用虚函数时程序将查看存储在对象中的虚函数表地址转向相应的虚函数表使用类声明中定义的第几个虚函数程序就使用数组的第几个函数地址并执行该函数。 //当父类的指针或者引用指向子类的对象时发生多态 Animal animal cat; //由于此处指向的是cat对象所以它会从cat对象的虚函数表中去找speak()函数在运行阶段发生了动态多态你传的是cat对象它就从cat对象的虚函数表中地址找该函数最后实现调用cat的speak()函数 animal.speak(); 类Animal的成员函数speak前不加virtual其size是1加virtual其size是4增加一个虚函数指针 当子类没有重写父类的虚函数时 子类重写父类的虚函数时子类的虚函数指针指向的地址将子类虚函数地址将父类的函数地址覆盖了动态多态实现地址的晚绑定。 多态案例-计算器类 //利用多态实现计算机类//多态的好处 //1、组织结构清晰 //2、可读性强 //3、对于前期和后期的扩展和维护性高#include iostream #include string using namespace std;class Abcal { public:virtual int getResult() { return 0; }int num1;int num2; };//设计加法计算器类 class Addcal : public Abcal { public:int getResult() { return num1 num2; } };//减法的计算器类 class Subcal : public Abcal { public:int getResult() { return num1 - num2; } };//乘法的计算器类 class Mulcal : public Abcal { public:int getResult() { return num1 * num2; } };void test() {//多态的使用//父类的指针或者引用指向子类的对象//加法计算Abcal a new Addcal; // new Addcal 创建加法计算器的对象 //堆区a-num1 10;a-num2 10;int c a-getResult();cout c c endl;//堆区手动开辟数据需要手动释放delete a; //只释放堆区的数据指针的类型没有变还是父类的指针//减法计算a new Subcal;a-num1 10;a-num2 10;int d a-getResult();cout d d endl;delete a; } int main() {test();return 0; } 虚函数的基本原理和概念 一、什么是虚函数 在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数 用法格式为virtual 函数返回类型 函数名参数表 {函数体}实现多态性通过指向派生类的基类指针或引用访问派生类中同名覆盖成员函数。 二、虚函数定义 简单地说那些被virtual关键字修饰的成员函数就是虚函数。虚函数的作用用专业术语来解释就是实现多态性Polymorphism多态性是将接口与实现进行分离用形象的语言来解释就是实现以共同的方法但因个体差异而采用不同的策略。下面来看一段简单的代码。 三、虚函数的作用 C中的虚函数的作用主要是实现了多态的机制。关于多态简而言之就是用父类型别的指针指向其子类的实例然后通过父类的指针调用实际子类的成员函数动态绑定。这种技术可以让父类的指针有“多种形态”这是一种泛型技术。所谓泛型技术说白了就是试图使用不变的代码来实现可变的算法。比如模板技术RTTI技术虚函数技术要么是试图做到在编译时决议要么试图做到运行时决议。 虚函数可以让子类对象在运行时动态地继承和修改父类的成员函数使得代码更加灵活、可重用并且可以实现多态性和抽象类等高级特性。 通过虚函数可以实现多态性Polymorphism即同一个函数名可以在不同的子类中表现出不同的行为这样可以提高代码的可重用性和灵活性。避免静态绑定在使用父类指针或引用调用子类对象的成员函数时如果没有使用虚函数则会进行静态绑定Static Binding即只能调用父类的成员函数无法调用子类特有的成员函数。虚函数的调用是动态绑定Dynamic Binding的即在运行时根据指针或引用所指向的对象类型来选择调用哪个函数从而实现动态多态性。抽象类是一种不能直接实例化的类只能被其他类继承并实现其虚函数。通过定义纯虚函数Pure Virtual Function可以使得一个类成为抽象类强制其子类必须实现该函数。 四、虚函数的底层实现原理 实现原理虚函数表虚表指针 虚函数表每个包含虚函数的类都会生成一个虚函数表Virtual Table其中存储着该类中所有虚函数的地址。虚函数表是一个由指针构成的数组每个指针指向一个虚函数的实现代码。虚函数指针在对象的内存布局中编译器会添加一个额外的指针称为虚函数指针或虚表指针Virtual Table Pointer简称 VTable 指针。这个指针指向该对象对应的虚函数表从而让程序能够动态地调用正确的虚函数。 当一个基类指针或引用调用虚函数时编译器会使用虚表指针来查找该对象对应的虚函数表并根据函数在虚函数表中的位置来调用正确的虚函数。 1、虚函数表的概念与作用 虚函数表是一种用于实现多态的机制在 C 中当一个类中包含虚函数时编译器会自动为该类生成一个虚函数表。这个表由包含虚函数的类的指针所指向其中每个条目都存储着对应虚函数的地址。当使用一个对象调用其虚函数时编译器通过该对象的虚函数表找到对应的虚函数地址并进行调用。 虚函数表的作用在于实现了多态即通过基类指针或引用调用派生类的虚函数实现了不同对象的不同行为。因此虚函数表使得程序可以更加灵活地处理不同类型的对象并支持代码的扩展和修改。同时由于虚函数表的存在C 中的虚函数调用比起其他语言如 Java中的成本要高因为需要额外的内存空间存储虚函数表及其指针。 2、虚函数指针的意义 虚函数指针的意义在于实现多态。多态是指同一种操作作用于不同的对象可以有不同的解释产生不同的执行结果。通过使用虚函数和虚函数指针可以在运行时确定调用哪个子类中的虚函数从而实现了动态绑定使得程序具有更好的灵活性和扩展性。 此外虚函数指针也是实现多级继承的关键在多级继承中每个子类都需要维护自己的虚函数表及其对应的虚函数指针。 3、虚函数的调用过程 在编译期间编译器会根据函数调用的类型和对象的类型确定要调用的函数。在运行期间程序会根据对象的实际类型来决定调用哪个函数。这个过程叫做动态绑定或者后期绑定。程序通过虚函数表vtable来实现动态绑定。每个含有虚函数的类都有自己的虚函数表存储了指向实际函数地址的指针。在对象被创建时它的指针会指向所属类的虚函数表。当调用虚函数时在对象中存储的指针会被解引用获取到虚函数表的地址。然后根据函数调用的类型从虚函数表中获取相应的函数地址。最后程序跳转到函数地址处执行实际的代码。由于是动态绑定所以调用的函数是根据对象实际类型来决定的。 4、虚函数的重写与覆盖 在 C 中派生类可以重写 (override) 它继承的虚函数这被称为函数的覆盖 (overriding)。当然子类也可以选择不重写基类的虚函数那么它将默认继承基类的实现这就是虚函数的重载 (overloading)。 虚函数的重写了解起来比较简单子类定义与父类相同名称和参数列表的虚函数此时会自动调用子类中定义的方法而不会调用父类的同名虚函数。例如 class Base { public:virtual void foo() {std::cout Base::foo() std::endl;} };class Derived : public Base { public:void foo() {std::cout Derived::foo() std::endl;} };int main() {Derived obj;Base ptr obj;ptr-foo(); // 输出Derived::foo()return 0; } 例子中派生类 Derived 重写了基类 Base 的虚函数 foo()所以在调用指针 ptr 的 foo() 时实际上调用的是 Derived 类中定义的函数输出结果为 Derived::foo()。 需要注意的是在子类中重写虚函数时其访问权限不能更严格即不能由 public 变为 private 或 protected否则编译器会报错。 五、使用虚函数后的变化 1 对象将增加一个存储地址的空间32位系统为4字节64位为8字节。 2 每个类编译器都创建一个虚函数地址表。 3 对每个函数调用都需要增加在表中查找地址的操作。 六、虚函数的注意事项 总结前面的内容 1 基类方法中声明了方法为虚后该方法在基类派生类中是虚的。 2 若使用指向对象的引用或指针调用虚方法程序将根据对象类型来调用方法而不是指针的类型。 3如果定义的类被用作基类则应将那些要在派生类中重新定义的类方法声明为虚。 构造函数不能为虚函数。基类的析构函数应该为虚函数。 首先明确调用顺序 构造函数先调用父类的再调用子类的不可以为虚函数折构函数先调用子类的再调用父类的可以是虚函数 友元函数不能为虚因为友元函数不是类成员只有类成员才能是虚函数。 如果派生类没有重定义函数则会使用基类版本。 重新定义继承的方法若和基类的方法不同协变除外会将基类方法隐藏如果基类声明方法被重载则派生类也需要对重载的方法重新定义否则调用的还是基类的方法。 虚函数在多态中的应用 虚函数在多态中的应用十分重要。多态指的是同一种类型的对象在不同的情况下可以表现出不同的行为。C通过使用虚函数来实现多态性。 虚函数是指在基类中被声明为虚函数并在派生类中重新定义的函数。当基类指针指向派生类对象时通过该指针调用虚函数时会根据指向的实际对象类型进行动态绑定从而执行相应的派生类函数。 例如有一个基类Animal和两个派生类Cat和Dog它们都有一个名为sound()的虚函数。当使用基类指针指向Cat或Dog对象时通过该指针调用sound()函数时会自动执行相应的派生类函数从而实现了多态性。 虚函数的应用使得程序具有更好的灵活性和可扩展性并且简化了代码的编写和维护。 虚函数的优缺点 虚函数的优点 实现多态性通过虚函数可以在不知道对象具体类型的情况下调用特定对象的方法。代码灵活性虚函数允许子类覆盖父类的方法并且不需要改变基类的代码。代码可维护性虚函数使得代码易于维护和扩展因为子类可以通过重载虚函数来实现自己的行为。 虚函数的缺点 额外的开销虚函数需要额外的开销来支持运行时的动态绑定和查找虚表。这可能会影响程序的性能。可能会引起混淆由于虚函数的存在同名的函数可能会被不同的类定义。如果没有正确的使用虚函数可能会导致混淆和错误的结果。不适合于小型应用虚函数对于小型应用来说可能过于复杂和冗余。在这种情况下使用虚函数可能会导致更多的开销而不是提高效率。 如何合理地使用虚函数 虚函数应用于继承层次结构中的多态性即通过基类指针或引用调用派生类对象的成员函数。可以将虚函数作为接口定义让不同的派生类实现自己的版本以满足各自的需求。避免在构造函数和析构函数中调用虚函数因为此时对象还未完全构造或已经被销毁。虚函数的声明应该在公共部分例如基类而不是在私有部分例如派生类中声明。将虚函数的实现定义为inline可以提高程序的执行效率。在使用纯虚函数时需要给出其具体实现。可以在派生类中实现也可以在基类中实现。避免过度使用虚函数因为虚函数会增加程序的开销。在没有必要的情况下可以使用普通成员函数代替虚函数。 纯虚函数和抽象类 纯虚函数是指在基类中定义的没有实现的虚函数。使用纯虚函数可以使该函数只有函数原型而没有具体的实现。注这里的“0”表示该函数为纯虚函数。 纯虚函数的作用是让子类必须实现该函数并且不能直接创建该类对象即该类为抽象类。 抽象类是包含纯虚函数的类它们不能被实例化只能被继承。抽象类只能用作其他类的基类。如果一个类继承了抽象类则必须实现所有的纯虚函数否则该类也会成为抽象类。 #include iostream #include string using namespace std;class Base { public://纯虚函数//只要有一个纯虚函数这个类称为抽象类//抽象类特点// 1、无法实例化对象// 2、抽象类的子类 必须要重写父类中的纯虚函数否则也是属于抽象类virtual void func() 0; //父类的虚函数实现毫无用处可以直接写成纯虚函数 };class Son : public Base { public:virtual void func() { cout func的调用 endl; } };void test(){Base *b new Son; //调用通过父类的指针指向子类的对象b-func(); //多态的目的让我们的函数接口更通用化通过一个父类指针可以调用不同对象下对应的函数delete b; }int main() {test();return 0; } 虚析构纯虚析构 虚析构函数的必要性 C虚析构函数的必要性在于当一个类拥有子类并且该类中包含有动态分配的内存资源时需要通过虚析构函数来释放这些内存资源。如果不使用虚析构函数当子类实例被删除时只会调用基类的析构函数而不会调用子类的析构函数从而导致子类中动态分配的内存资源无法正确释放可能会导致内存泄漏或者程序崩溃。因此在使用继承和动态内存分配时为了保证程序的正确性和健壮性需要使用虚析构函数来释放内存资源。(将父类的析构函数写为虚函数或者纯虚函数即可可以解决父类指针释放子类对象时不干净的问题) 纯虚析构 需要声明也需要实现有了纯虚析构之后这个类也属于抽象类无法实例化对象。 多重继承中的虚函数 在多重继承中如果一个类同时继承了多个基类而这些基类中都有同名的虚函数那么子类必须对这些虚函数进行重写并实现。此时需要使用作用域限定符来指明重写的是哪个基类的虚函数。 例如 class Base1 { public:virtual void func() { cout Base1::func() endl; } };class Base2 { public:virtual void func() { cout Base2::func() endl; } };class Derived : public Base1, public Base2 { public:virtual void func() { Base1::func(); Base2::func(); } }; 例子中派生类Derived同时继承了Base1和Base2这两个基类中都有名为func的虚函数。在Derived中我们通过使用作用域限定符Base1::和Base2::分别调用了两个基类中的虚函数。 Derive d; Base1 *b1 d; Base2 *b2 d; Base3 *b3 d; b1-f(); //Derive::f() b2-f(); //Derive::f() b3-f(); //Derive::f() b1-g(); //Base1::g() b2-g(); //Base2::g() b3-g(); //Base3::g()小结 摘自 1、对C虚函数不了解看完这篇文章掌握虚函数的原理和作用 - 知乎 2、C虚函数的作用和实现原理_c虚函数作用及底层原理-CSDN博客 3、C虚函数的作用和实现原理_c 纯虚函数 父类实现-CSDN博客 4、54 类和对象-多态-多态的原理剖析_哔哩哔哩_bilibili