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

C++ 虚函数若干问题

虚函数场景

一、最基本常见的

<pre name="code" class="cpp">#include <iostream>
using namespace std;


class CBase
{
public:
  virtual void print()
  {
    cout<< "base" << endl;
  }
  void DoPrint()
  {
    print();
  }
};


class CChild1: public CBase
{
public:
    virtual void print()    //virtual is optional;看类的成员函数是不是虚函数要看这个类的最顶层父类的同名同签名函数前有没有virtual,如果没有父类,则看自己前面有没有virtual;
      {
          cout<< "child1" << endl;
      }
};


class CChild2: public CBase
{
public:
  virtual void print()      //virtual is optional;看类的成员函数是不是虚函数要看这个类的最顶层父类的同名同签名函数前有没有virtual,如果没有父类,则看自己前面有没有virtual;
  {
      cout<< "child2" << endl;
  }
};


void DoPrint(CBase *base)
{
  base->DoPrint();
}


int main()
{
  CBase* base = new CBase();
  CChild1* child1 = new CChild1();
  CChild2* child2 = new CChild2();

  DoPrint(child1);
  DoPrint(child2);
  DoPrint(base);

  delete base;

  base = child1;  // 上面只是删除了base所指向的对象,base指针本身存在
  base->print();  // 传统的虚函数的用法。

  delete child1;
  delete child2;

  return 0;
}


 

运行结果:

二、需要对虚函数理解很透彻才能看懂的一个例子

#include <iostream>

using namespace std;

class BaseClass{
public:
    BaseClass()
    {
        cout<<"BaseClass's constructor."<<endl;
    }
    virtual ~BaseClass()
    {
        cout<<"BaseClass's destructor."<<endl;
    }


    virtual void virtualFunction()
    {
        cout<<"BaseClass's virtualFunction"<<endl;
    }

    void test()
    {
        virtualFunction();
    }
};

class DerivedClass:public BaseClass
{
public:
    virtual void virtualFunction()        //virtual is optional;
    {
        cout<<"DerivedClass's virtualFunction"<<endl;
    }

};

int main()
{
    cout << "Hello world!" << endl<<endl;

    DerivedClass derive;
    derive.test();

    return 0;
}


运行结果:



三、你可以直接跳过的例子

#include <iostream>
using namespace std;

class A {
public:
    void foo() {
        cout << "A's foo()" << endl;
    }
};

class B: public A {
public:
    virtual void foo() {
        cout << "B's foo()" << endl;
    }
    void bTest()
    {
        cout<<"B::bTest() "<<endl;
        foo();
    }
};

class C: public B {
public:
    void foo() override {        // override can only qualify virtual function.its function mainly lie on complication tips.
        cout << "C's foo()" << endl;
    }
};


int main() {
    C cobj;
    B *bptr = &cobj;
    bptr->foo();
    bptr->bTest();
    A* aptr = &cobj;
    aptr->foo();
}

运行结果:如果在A的foo前面加virtual则最后输出的是C’s foo();看类的成员函数是不是虚函数要看这个类的最顶层父类的同名同签名函数前有没有virtual,如果没有父类,则看自己前面有没有virtual;至于第三个输出C‘s foo()在第二个例子已经说明;


#include <iostream>
using namespace std;

class A {
public:
    virtual void foo() {
        cout << "A's foo()" << endl;
        bar();
    }
    virtual void bar() {
        cout << "A's bar()" << endl;
    }
};

class B: public A {
public:
    void foo() {
        cout << "B's foo()" << endl;
        A::foo();
    }
    void bar() {
        cout << "B's bar()" << endl;
    }
};

int main() {
    B bobj;
    A *aptr = &bobj;
    aptr->foo();
    A aobj = *aptr;    //  这个例子想说的关键部分:将子类对象复制给基类对象,发生切割,就是只复制了两类中相同成员部分。
    aobj.foo();
}

运行结果:



零碎知识点

1、看类的成员函数是不是虚函数要看这个类的最顶层父类的同名同签名函数前有没有virtual,如果没有父类,则看自己前面有没有virtual。所以,如果顶层父类的某一个函数前有virtual关键字,那么不管下面的子类的同名同签名函数前有没有virtual关键字,子类中的这个函数都是虚函数。

2、override只能用来限定虚函数,它的作用更多的是起编译检查作用,比如如果你在一个函数后面加override关键字,那么编译器就会检查这个函数和父类中的同名函数签名是不是一样,如果不一样就报错,如果没有override关键字,签名又是不一样的,并且在这个函数签名有virtual关键字,那么这个函数就成了一个新的虚函数。如果感兴趣,可以看下这篇博客关于override的博客。


在构造函数中调用虚函数From C++'s father's homepage




什么情况下析构函数该声明为virtual及其原因

        首先你该看下C++之父BS对这个问题的解释。

       总结如下:

       1、当类中存在virtual函数的时候,就该把类的析构函数声明成virtual的。因为类中存在virtual函数表明设计者有把这个类作为基类的意图。

       2、把可能作为基类的类的析构函数声明成virtual的原因是,当基类指针指向子类对象时,如果基类析构函数不是virtual的,delete指向子类对象的基类指针时将直接调用基类的析构函数而不是子类的析构函数,这样子类就存在资源没有释放的可能。将基类析构函数声明成virtual的,delete指向子类对象的基类指针时调用的是子类的析构函数(在子类的析构函数调用前会调用基类的析构函数),确保资源的释放。


虚函数表机制

他山之石:http://www.cnblogs.com/chinazhangjie/archive/2012/07/11/2586535.html

更专业的解释请查看:Inside the C++ Object Model[Stanley B. Lippman] Chapter 4


原文:http://www.cnblogs.com/taoxu0903/archive/2008/02/04/1064234.html



相关文章:

  • 微软亚太研发集团2013招聘
  • QT 获取系统屏幕分辨率
  • QSS 用法介绍
  • 简单算法--求一个数n的二进制形式的第i位
  • #pragam once 和 #ifndef 预编译头
  • 在线编程学习【优质的资源】
  • QSizeploicy、 QLayout-setSizeConstrain
  • C语言程序员必读的5本书
  • 一个值得Geek收藏的网站
  • 如何在QML中调用C++的方法并接收C++的信号
  • 25个非常实用的jQuery/CSS3应用组件
  • Qt for android basis , Qt Android 开发基础
  • Qt 应用程序打包教程
  • ForThirdWork-No.1:C/C++笔试题回忆并整理
  • ForThirdWork-No.2:C/C++笔试题回忆并整理
  • [译] 怎样写一个基础的编译器
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【5+】跨webview多页面 触发事件(二)
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • happypack两次报错的问题
  • js 实现textarea输入字数提示
  • js作用域和this的理解
  • Laravel 菜鸟晋级之路
  • Mithril.js 入门介绍
  • Object.assign方法不能实现深复制
  • React+TypeScript入门
  • Redis 懒删除(lazy free)简史
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • 复杂数据处理
  • 前端js -- this指向总结。
  • 前端面试总结(at, md)
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 我的面试准备过程--容器(更新中)
  • 自动记录MySQL慢查询快照脚本
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • mysql面试题分组并合并列
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (Java)【深基9.例1】选举学生会
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (正则)提取页面里的img标签
  • (转)使用VMware vSphere标准交换机设置网络连接
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net FrameWork简介,数组,枚举
  • .Net Web窗口页属性
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .net和jar包windows服务部署
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • [【JSON2WEB】 13 基于REST2SQL 和 Amis 的 SQL 查询分析器
  • [BSGS算法]纯水斐波那契数列
  • [C++核心编程](四):类和对象——封装