zoukankan      html  css  js  c++  java
  • C++中的虚函数(1)

    虚函数语法形式

    class Foo{
        ...
        virtual void func();
        ...
    };

    C++虚函数必须和C++的继承结合起来一起看。示例是最好的解释。

    #include <iostream>
    using namespace std;
    class Base{
        public:
            void func1(){
                cout<<"I'm Base func1"<<endl;
            }
            virtual void func2(){
                cout<<"I'm Base func2"<<endl;
            }
    };
    
    class Derived:public Base{
        public:
            void func1(){
                cout<<"I'm Derived func1"<<endl;
            }
            virtual void func2(){
                cout<<"I'm Derived func2"<<endl;
            }
    };
    
    int main(){
        Derived d= Derived();
        Base &o = d;            //创建了一个指向子类对象的引用
        o.func1();              //func1不是虚函数,因此使用了Base的func1方法
        o.func2();              //func2是虚函数,因此根据引用所指向的实际对象的类型选择func2,即Derived的func2
        return 0;
    }

    运行结果

    I'm Base func1
    I'm Derived func2

    汇编代码分析

      40091c:       48 c7 45 f0 00 00 00    movq   $0x0,-0x10(%rbp)          #-0x10(%rbp)中存放的是对象d,这个对象占用8个字节
      400923:       00                                                       #这8个字节将会用来存储一个指针,指针指向Derived类的虚函数表
      400924:       48 8d 45 f0             lea    -0x10(%rbp),%rax
      400928:       48 89 c7                mov    %rax,%rdi
      40092b:       e8 18 01 00 00          callq  400a48 <_ZN7DerivedC1Ev>  #调用Derived的默认构造函数
      400930:       48 8d 45 f0             lea    -0x10(%rbp),%rax
      400934:       48 89 45 f8             mov    %rax,-0x8(%rbp)           #-0x8(%ebp)存储引用o,o的内容实际上是对象d的起始地址。
      400938:       48 8b 45 f8             mov    -0x8(%rbp),%rax
      40093c:       48 89 c7                mov    %rax,%rdi                 #这两行准备this指针
      40093f:       e8 70 00 00 00          callq  4009b4 <_ZN4Base5func1Ev  #不是虚函数,直接调用Base的func1
      400944:       48 8b 45 f8             mov    -0x8(%rbp),%rax           #取出d的地址
      400948:       48 8b 00                mov    (%rax),%rax               #取出d的内容,即指向Derived类虚函数表的指针
      40094b:       48 8b 10                mov    (%rax),%rdx               #func2是Derived类的唯一虚函数,即第0项。取出func2的地址。
      40094e:       48 8b 45 f8             mov    -0x8(%rbp),%rax
      400952:       48 89 c7                mov    %rax,%rdi                 #这两行也是准备this指针
      400955:       ff d2                   callq  *%rdx                     #调用func2

    如果感兴趣,可以用gdb实际调试一下,观察运行的过程。

    NOTE 1: 当子类不override父类的虚函数,那么子类就自动继承父类的虚函数。

    NOTE 2: 虚函数也可以有默认参数。如果用父类的指针或引用指向子类的对象,那么调用的函数是子类的,使用的默认参数却是父类函数的。(C++真是无比混乱啊!)

    NOTE 3: 如果不想动态调用虚函数,可以使用类名+'::'来限定调用哪个函数。例如,上例中可以添加语句o.Base::func2()来调用父类的func2。

  • 相关阅读:
    Android监听系统短信数据库变化-提取短信内容
    Android 短信拦截及用途分析
    ListView:The content of the adapter has changed but ListView did not receive a notification终极解决方法
    Activity onDestroy() 回调缓慢问题分析及完美解决方案
    获取View的截图-将View转换为Bitmap对象
    Android正则表达式使用及性能隐患分析
    Android Activity返回键控制的两种方式
    Android TextView 添加下划线的几种方式
    android gridview几个重要属性(android:listSelector自带内部padding分析)
    ADB server didn't ACK
  • 原文地址:https://www.cnblogs.com/richardustc/p/2988449.html
Copyright © 2011-2022 走看看