Virtual Function

虚函数,直接上个代码吧,这份代码很长,因为其中研究了一下虚函数的实现,虽说好像什么也没懂。

#include <iostream>
#include <stdio.h>
using namespace std;

class Base {
  
  public:
    Base() {}
    virtual ~Base() {}
    void non_virtual_show() {
        cout << "it shows I am a Base(non_virtual)" << endl;
    }
    virtual void virtual_show() {
        cout << "it shows I am a Base(virtual)" << endl;
    }
    virtual void virtual_show_1() {}
    virtual void virtual_show_2() {
        cout << "successfully call virtual_show_2 in base" << endl;
    }
};

class Derive_1 : public Base {
    
  public:
    void non_virtual_show() {
        cout << "it shows I am a Derive_1(non_virtual)" << endl;
    }
    virtual void virtual_show() {
        cout << "it shows I am a Derive_1(virtual)" << endl;
    }
    virtual void virtual_show_1() {}
    virtual void virtual_show_2() {
        cout << "successfully call virtual_show_2 in derive_1" << endl;
    }
};

int main() {
    
    //PART 1 without pointer
    Base b1;
    Derive_1 d1;
    b1.non_virtual_show();
    b1.virtual_show();
    d1.non_virtual_show();
    d1.virtual_show();
    cout << endl;
    
    //PART 2 with pointer
    Base *p1 = new Base;
    Base *p2 = new Derive_1;
    Derive_1 *p3 = new Derive_1;
    //Derive_1 *p4 = new Base; invalid conversion
    p1->non_virtual_show();
    p1->virtual_show();
    p2->non_virtual_show();
    p2->virtual_show();
    cout << endl;
    
    //PART 3 address
    void (Base::*B_pointer)();
    B_pointer = &Base::non_virtual_show;
    printf("%p\n", B_pointer);
    B_pointer = &Base::virtual_show;
    printf("%p\n", B_pointer);
    B_pointer = &Base::virtual_show_1;
    printf("%p\n", B_pointer);
    B_pointer = &Base::virtual_show_2;
    printf("%p\n", B_pointer);
    cout << endl;
    
    void (Derive_1::*D_pointer)();
    D_pointer = &Derive_1::non_virtual_show;
    printf("%p\n", D_pointer);
    D_pointer = &Derive_1::virtual_show;
    printf("%p\n", D_pointer);
    D_pointer = &Derive_1::virtual_show_1;
    printf("%p\n", D_pointer);
    D_pointer = &Derive_1::virtual_show_2;
    printf("%p\n", D_pointer);
    cout << endl;
    
    //PART 4 call the virtual function through their address
    void (Base::*pointer1)();
    Base *p4 = new Derive_1;
    pointer1 = &Base::non_virtual_show;
    (p4->*pointer1)();
    pointer1 = &Base::virtual_show;
    printf("%p\n", pointer1);
    pointer1 = &Base::virtual_show_1;
    printf("%p\n", pointer1);
    pointer1 = &Base::virtual_show_2;
    printf("%p\n", pointer1);
    (p4->*pointer1)();
    (p4->*(&Base::virtual_show_2))();
    (p4->Base::virtual_show_2)();
    p4->Base::virtual_show_2();
    //pointer1 = &Derive_1::non_virtual_show; cannot convert 'void (Derive_1::*)' to 'void (Base::*)()' in assignment
    //pointer1 = &Derive_1::virtual_show; cannot convert 'void (Derive_1::*)' to 'void (Base::*)()' in assignment
    cout << endl;
    
    void (Base::*pointer2)();
    Derive_1 *p5 = new Derive_1;
    pointer2 = &Base::non_virtual_show;
    (p5->*pointer2)();
    pointer2 = &Base::virtual_show;
    printf("%p\n", pointer2);
    pointer2 = &Base::virtual_show_1;
    printf("%p\n", pointer2);
    pointer2 = &Base::virtual_show_2;
    printf("%p\n", pointer2);
    (p5->*pointer2)();
    (p5->*(&Base::virtual_show_2))();
    (p5->Base::virtual_show_2)();
    p5->Base::virtual_show_2();
    cout << endl;
    
    void (Derive_1::*pointer3)();
    Derive_1 *p6 = new Derive_1;
    pointer3 = &Base::non_virtual_show;
    (p6->*pointer3)();
    pointer3 = &Base::virtual_show;
    printf("%p\n", pointer3);
    pointer3 = &Base::virtual_show_1;
    printf("%p\n", pointer3);
    pointer3 = &Base::virtual_show_2;
    printf("%p\n", pointer3);
    (p6->*pointer3)();
    (p6->*(&Base::virtual_show_2))();
    (p6->Base::virtual_show_2)();
    p6->Base::virtual_show_2();
    
    return 0;
}
输出结果:


首先是基本的解释虚函数:
看part 1 和 part 2
p2->non_virtual_show();
p2->virtual_show();
这两行代码说明了虚函数的不同之处,上一行在静态编联(编译阶段)中已经确定调用的函数,而下一行是动态编联,在程序运行阶段才确定调用哪个类中的函数,虽说p2指向基类,但是类对象在程序运行过程中才确定是派生类,这样通过虚函数就会调用派生类中的函数。
摘抄个内容吧:
实现 动态联编需要三个条件:
1、 必须把 动态联编的行为定义为类的虚函数。
2、 类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来。
3、 必须先使用 基类 指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。
简单地说,那些被virtual关键字修饰的 成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现 多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。
定义虚函数的限制:
(1)非 类的成员函数不能定义为虚函数,类的成员函数中 静态成员函数和 构造函数也不能定义为虚函数,但可以将 析构函数定义为虚函数。实际上,优秀的程序员常常把 基类析构函数定义为虚函数。因为,将 基类析构函数定义为虚函数后,当利用delete删除一个指向 派生类定义的 对象指针时,系统会调用相应的类的析构函数。而不将 析构函数定义为虚函数时,只调用 基类的析构函数。
(2)只需要在声明函数的类体中使用 关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。
(3)当将 基类中的某一 成员函数声明为虚函数后, 派生类中的同名函数自动成为虚函数。
(4)如果声明了某个 成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数。在以该类为 基类派生类中,也不能出现这种同名函数。

以上就是虚函数的基本东西了,那么part3和part4是什么东西?
其实part3part4就是这个问题:
 为什么会输出不一样的结果呢?
百度百科上提到了 virtual table,参考 http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
于是写了part3part4研究看看,
运行结果:

首先是part3的部分,发现不是虚函数的地址和虚函数的地址差得很多 ,而且,当我把基类的析构函数的virtual去掉之后的输出结果:

 这似乎说明,虚函数的所谓地址只是virtual table中的相对地址(猜测是下标),因为间隔都是一样的,而且基类和派生类的相对地址是一样的(但这并不能说明地址一样,只是相对于virtual table起始距离一样)。不知道这种情况应该怎么解释。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
虚函数是面向对象编程中的概念,它允许在基类中声明一个函数,在派生类中进行重写。通过使用虚函数,可以实现多态性,即在运行时根据对象的实际类型来调用对应的函数。 在C++中,通过在基类的成员函数前面加上关键字"virtual"来声明一个虚函数。派生类可以重写基类中的虚函数,并使用相同的函数签名来定义自己的实现。当通过基类指针或引用调用虚函数时,程序会根据实际对象的类型来调用对应的重写函数。 例如,假设有一个基类Animal和派生类Dog和Cat,它们都有一个虚函数speak()。在基类Animal中,我们可以声明这个函数为虚函数,然后在派生类中分别重写它,使得不同的动物可以发出不同的声音。 ```cpp class Animal { public: virtual void speak() { cout << "Animal speaks!" << endl; } }; class Dog : public Animal { public: void speak() override { cout << "Dog barks!" << endl; } }; class Cat : public Animal { public: void speak() override { cout << "Cat meows!" << endl; } }; int main() { Animal* animal1 = new Dog(); Animal* animal2 = new Cat(); animal1->speak(); // Output: "Dog barks!" animal2->speak(); // Output: "Cat meows!" delete animal1; delete animal2; return 0; } ``` 在上面的例子中,通过基类指针animal1和animal2分别指向Dog和Cat对象,然后调用它们的虚函数speak()。由于speak()函数是虚函数,所以程序会根据实际对象的类型来调用相应的重写函数,输出不同的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值