C++多态–虚函数virtual及override

C++多态–虚函数virtual及overrideC 多态 C 多态 polymorphism 是通过虚函数来实现的 虚函数允许子类重新定义成员函数 而子类重新定义父类的做法称为覆盖 override 或者称为重写 最常见的用法就是声明基类的指针 利用该指针指向任意一个子类对象 调用相应的虚函数 动态绑定 由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数 所以被成为 虚 函数 如果没有使用虚函数的话 即没有利用 C

C++多态

C++多态(polymorphism)是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。

最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,动态绑定。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。

用下面代码演示多态和非多态。

#include 
  
    using namespace std; class A { public: void foo() { printf("1\n"); } virtual void fun() { printf("2\n"); } }; class B : public A { public: void foo() //隐藏:派生类的函数屏蔽了与其同名的基类函数 { printf("3\n"); } void fun() //多态、覆盖 { printf("4\n"); } }; int main(void) { A a; B b; A *p = &a; p->foo(); //输出1 p->fun(); //输出2 p = &b; p->foo(); //取决于指针类型,输出1 p->fun(); //取决于对象类型,输出4,体现了多态 return 0; } 
  

C++纯虚函数及虚函数

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0 

引入纯虚函数的原因:

       (1)为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

       (2)在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 

包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。

       虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。

虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。如果父类或者祖先类中函数func()为虚函数,则子类及后代类中,函数func()是否加virtual关键字,都将是虚函数。为了提高程序的可读性,建议后代中虚函数都加上virtual关键字。

C++保留字override

       override 仅在成员函数声明之后使用时才是区分上下文的且具有特殊含义;否则,它不是保留的关键字。使用 override 有助于防止代码中出现意外的继承行为。以下示例演示在未使用override 的情况下,可能不打算使用派生类的成员函数行为。编译器不会发出此代码的任何错误。

class BaseClass { virtual void funcA(); virtual void funcB() const; virtual void funcC(int = 0); void funcD(); }; class DerivedClass: public BaseClass { virtual void funcA(); // ok, works as intended virtual void funcB(); // DerivedClass::funcB() is non-const, so it does not // override BaseClass::funcB() const and it is a new member function virtual void funcC(double = 0.0); // DerivedClass::funcC(double) has a different // parameter type than BaseClass::funcC(int), so // DerivedClass::funcC(double) is a new member function };


当使用 override时,编译器会生成错误,而不会在不提示的情况下创建新的成员函数。

class BaseClass { virtual void funcA(); virtual void funcB() const; virtual void funcC(int = 0); void funcD(); }; class DerivedClass: public BaseClass { virtual void funcA() override; // ok virtual void funcB() override; // compiler error: DerivedClass::funcB() does not // override BaseClass::funcB() const virtual void funcC( double = 0.0 ) override; // compiler error: // DerivedClass::funcC(double) does not // override BaseClass::funcC(int) void funcD() override; // compiler error: DerivedClass::funcD() does not // override the non-virtual BaseClass::funcD() };

C++多态--虚函数virtual及override

下面代码展示了手动调用虚函数的过程

#include 
   
     using namespace std; class A { public: virtual void vfunc1() { cout << "A::vfunc1()" << endl; }; virtual void vfunc2() { cout << "A::vfunc2()" << endl; }; void func1() { cout << "A::func1()" << endl; }; void func2() { cout << "A::func2()" << endl; }; private: int data1_; int data2_; }; class B :public A { public: virtual void vfunc1() override { cout << "B::vfunc1()" << endl; }; void func2() { cout << "B::func2()" << endl; }; private: int data3_; }; class C :public B { public: virtual void vfunc1() override { cout << "C::vfunc1()" << endl; }; void func2() { cout << "C::func2()" << endl; }; private: int data1_, data4_; }; //演示了手动调用虚函数的过程 int main() { B a; typedef void(*Fun)(void); Fun pFun = nullptr; cout << "虚函数表地址:" << (int*)(&a) << endl; cout << "虚函数表第1个函数地址:"<<(int*)*(int*)(&a) << endl; cout << "虚函数表第2个函数地址:" << (int*)*(int*)(&a) + 1 << endl; pFun = (Fun)*((int*)*(int*)(&a)); pFun(); pFun = (Fun)*((int*)*(int*)(&a) + 1); pFun(); return 0; } 
   

参考文章:

[1] http://blog.csdn.net/hackbuteer1/article/details/

[2] http://blog.csdn.net/hackbuteer1/article/details/

[3] http://www.jb51.net/article/78489.htm


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214929.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午3:06
下一篇 2026年3月18日 下午3:07


相关推荐

  • SQL Server 2016 列存储索引功能增强「建议收藏」

    SQL Server 2016 列存储索引功能增强「建议收藏」列存储索引(columnstoreindex)在SQLServer2012中已经引入,其带来性能提升的同时也有很多限制,比如对带有列存储索引的表进行INSERT,UPDATE和DELETE时,会遇到如下错误提示:由于这种限制,索引列存储索引并不太适合在OLTP中应用。不过,SQLServer2016对列存储索引做了很多改进,其中我觉得最大的变化是可更新的

    2022年7月14日
    18
  • 策略模式Strategy——坐什么车回家?

    策略模式Strategy——坐什么车回家?

    2022年1月4日
    50
  • 深入Redis客户端(redis客户端属性、redis缓冲区、关闭redis客户端)「建议收藏」

    深入Redis客户端(redis客户端属性、redis缓冲区、关闭redis客户端)「建议收藏」Redis数据库采用I/O多路复用技术实现文件事件处理器,服务器采用单线程单进程的方式来处理多个客户端发送过来的命令请求,它同时与多个客户端建立网络通信。服务器会为与它相连接的客户端创建相应的redis.h/redisClient结构,在这个结构中保存了当前客户端的相关属性及执行相关功能时的数据结构。I/O多路复用:linux有五类io模型1.阻塞2.非阻塞3…

    2022年6月10日
    30
  • compound extremes_conscientiousness

    compound extremes_conscientiousnessextremeComponents是一个好用的表格插件,可以方便的对分页,导出excel等进行操作并且可以和任何框架进行集成,包括springmvc,struts,struts2等

    2022年8月20日
    14
  • CAN协议通信「建议收藏」

    CAN协议通信「建议收藏」CAN协议广泛应用于车辆诊断通信中,学习它的过程,就像学习一种与车辆沟通的语言,让我更深入的了解汽车的世界。感受汽车的精彩

    2022年6月28日
    65
  • 渲染杂谈:early-z、z-culling、hi-z、z-perpass到底是什么?

    渲染杂谈:early-z、z-culling、hi-z、z-perpass到底是什么?渲染杂谈 early z z culling hi z z perpass 到底是什么 之前一直被这几个和深度缓存 z buffer 相关的概念搞得神魂颠倒 今天在翻阅 Real TimeRenderin 时碰巧碰巧看到了这部分的讲解 硬着头皮看了看 姑且算是讲几个概念分清楚了 以我的记性估计下周就全忘了 所以打算顺手记下来 这四种技术本质上都是解决传统渲染管线中的同一个问题 过度绘制 OverDraw 一个经典的渲染管线通常会依次经历顶点阶段 光栅化 片元阶段和逐像素处理 其中片元阶段会进

    2025年10月20日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号