Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 理解虚函数有困难_C++ - Fatal编程技术网

C++ 理解虚函数有困难

C++ 理解虚函数有困难,c++,C++,及 我读到,h指针将指向它所指向的对象的vtable,并因此调用该函数?但这是如何在运行时实现的呢 除非确定正在执行的操作,否则不应尝试访问vtable指针。就我们通常用来定义程序含义的语言而言,vtable甚至不存在。这是一个实现细节,它属于实现(也称为编译器和运行时环境) 如果实现受到可移植ABI(应用程序二进制接口)的约束,那么ABI将说明在哪里找到vtable指针以及vtable内的内容reinterpret_cast(my_obj)应该能够从任何“合理”ABI上的对象获取指针 这样一个

我读到,
h
指针将指向它所指向的对象的vtable,并因此调用该函数?但这是如何在运行时实现的呢

  • 除非确定正在执行的操作,否则不应尝试访问vtable指针。就我们通常用来定义程序含义的语言而言,vtable甚至不存在。这是一个实现细节,它属于实现(也称为编译器和运行时环境)

    如果实现受到可移植ABI(应用程序二进制接口)的约束,那么ABI将说明在哪里找到vtable指针以及vtable内的内容
    reinterpret_cast(my_obj)
    应该能够从任何“合理”ABI上的对象获取指针

    这样一个程序将被限制在一个平台和一个ABI上。(C++ABI接口的变化频率往往比C语言高,但比其他语言低。)依赖ABI是一个糟糕的设计选择,除非你只是想证明自己疯了

  • vtable标识派生类-这就是它的用途。它包含指向由重写基的派生类实现的函数的指针。它还包含一个具有派生类名称的结构,并链接到它的基,以便动态地确定它的派生源

    dynamic_cast
    用于确定派生和查找派生对象的算法实际上可能非常慢,而不是O(1)。它通常必须搜索基类的链接结构


  • 好的,你的第一个问题:我给你举个例子,可能会更好的理解!

    p1将指向vtale1,p2将指向vtable2,p3将指向vtable3,如果调用某个虚拟函数,它将找到虚拟表并获取地址

    在代码中:

    Base1 *p1 = new Derive();
    Base2 *p2 = new Derive();
    Base3 *p3 = new Derive();
    
    h将指向Y的内存的起始位置,即X的虚拟表,他将找到在Y中实现的
    abc()
    的地址

    你的第二个问题:

    编译器会考虑成员函数作为常态函数,因此将成员函数的地址放在<代码>代码段中,所以它不占用内存!p> 如果您想读取虚拟表,可以这样尝试:我在gcc4.7中的示例中尝试了这种方法

    X *h = new Y;
    
    我是否可以阅读这个虚拟表格

    不是真的,不是不知道指针相对于对象指针值的位置,对象指针值是特定于编译器的

    如果没有,至少可以访问虚拟指针

    为什么??您可以通过
    h->abc
    获取函数的地址,这就是您想要的吗

    另外,当我调用h->abc()时,它如何知道h所指向的类的对象

    它实际上不知道,它只知道该类的vtable在哪里。如果使用RTTI,vtable中有信息可以告诉它是什么类,但是调用虚拟函数并不需要这些信息。从X派生的每个类都有自己的vtable,其中包含自己的虚拟函数指针。(当然,始终假定基于vtable的实现。)

    我读到,h指针将指向它所指向的对象的vtable,并因此调用该函数?但这是如何在运行时实现的呢

    你刚才自己也描述过。仅稍微详细说明一下,指针
    h->abc
    解析为
    h->\u vtable[x]
    ,表示
    abc
    的虚拟函数指针vtable中的偏移量的某个常量
    x
    。因此,调用解析为
    *(h->_vtable[abc])(…)

    另一个问题,与虚拟函数无关,我需要澄清一下。若函数和其他变量一样有地址,为什么它们不占用对象中的空间呢


    他们为什么要这样做?这意味着每个对象中的每个函数都有一个副本。重点是什么?

    我将编辑最后一个问题,因为它与讨论的主题不符。但我相信前两个问题完全在讨论范围之内。
    X *h = new Y;
    h->abc();/* This would call the abc function in Y*/
    
    #include<iostream>
    using namespace std;
    class Base1 {
    
        public:
    
            int ibase1;
    
            Base1():ibase1(10) {}
    
            virtual void f() { cout << "Base1::f()" << endl; }
    
            virtual void g() { cout << "Base1::g()" << endl; }
    
            virtual void h() { cout << "Base1::h()" << endl; }
    };
    
    class Base2 {
    
        public:
    
            int ibase2;
    
            Base2():ibase2(20) {}
    
            virtual void f() { cout << "Base2::f()" << endl; }
    
            virtual void g() { cout << "Base2::g()" << endl; }
    
            virtual void h() { cout << "Base2::h()" << endl; }
    
    };
    
    class Base3 {
    
        public:
    
            int ibase3;
    
            Base3():ibase3(30) {}
    
            virtual void f() { cout << "Base3::f()" << endl; }
    
            virtual void g() { cout << "Base3::g()" << endl; }
    
            virtual void h() { cout << "Base3::h()" << endl; }
    };
    
    class Derive : public Base1, public Base2, public Base3 {
    
        public:
    
            int iderive;
    
            Derive():iderive(100) {}
    
            virtual void f() { cout << "Derive::f()" << endl; }
    
            virtual void g1() { cout << "Derive::g1()" << endl; }
    
    };
    
    Base1 *p1 = new Derive();
    Base2 *p2 = new Derive();
    Base3 *p3 = new Derive();
    
    X *h = new Y;
    
    typedef void(*Func)(void);
        Derive d;
        int **pd = (int **)(&d);
        int i = 0;
        while(i < 4)
        {
            Func f = (Func)pd[0][i];
            f();
            i++;
        }
        int s = (int)(pd[1]);
        cout << s << endl;
        i = 0;
        cout << "===============================================" << endl;
        while(i < 3)
        {
            Func f = (Func)pd[2][i];
            f();
            i++;
        }
        s = (int)(pd[3]);
        cout << s << endl;
        cout << "===============================================" << endl;
        i = 0;
        while(i < 3)
        {
            Func f = (Func)pd[4][i];
            f();
            i++;
        }
        s = (int)(pd[5]);
        cout << s << endl;
        s = (int)(pd[6]);
    cout << s << endl;
    
     Derive::f()
    Base1::g()
    Base1::h()
    Derive::g1()
    10
    ===============================================
    Derive::f()
    Base2::g()
    Base2::h()
    20
    ===============================================
    Derive::f()
    Base3::g()
    Base3::h()
    30
    100