6

以下段落摘自 Stroustup 书“The C++ Programming Language”(第三版)的第 420 页:

因为指向虚拟成员(本例中为 s)的指针是一种偏移量,所以它不依赖于对象在内存中的位置。因此,只要在两个地址空间中使用相同的对象布局,指向虚拟成员的指针就可以安全地在不同地址空间之间传递。与指向普通函数的指针一样,指向非虚拟成员函数的指针不能在地址空间之间交换。

我对本段的最后一句话提出异议。下面,您将找到一个代码片段,其中指向非虚拟成员函数foo()和的指针在一个基础对象和派生对象foo1()之间进行交换,没有问题。ab

不能重载基类foo()foo1()派生类中的任何函数,因为在这种情况下,编译器将发出如下所示的错误。

#include <iostream>

class A
{
    int i;
    public:
    A() : i(1) {}
    void foo() { std::cout << i << '\n'; }
    void foo1() { std::cout << 2 * i << '\n'; }
};

class B: public A
{
    int j;
    public:
    B() : A(), j(2) {}
//  void foo() { std::cout << j << '\n'; }
};

int main()
{
    typedef void (A::* PMF)();
    PMF p = &B::foo;    //   error C2374: 'p' redefinition, multiple initialization
                        //   if foo() is overloaded in B.
    PMF q = &B::foo1;
    B b;
    (b.*p)();
    (b.*q)();

    A a;
    (a.*p)();
    (a.*q)();
}
4

2 回答 2

1

那句话是正确的:在(标准)C++ 中,一个程序,或者更确切地说是进程,只有一个地址空间。所以正如 ulidtko 所指出的,这句话是指在不同进程的地址空间之间交换指向虚拟与非虚拟成员函数的指针的可能性。

类的非虚拟成员函数几乎是一个标准函数,带有一个隐含参数到您调用它的对象(this指针)。因此,它会在加载时在进程的地址空间中分配一些地址。它最终在您的地址空间中的确切位置当然取决于您的平台以及该成员函数是否是动态链接库的一部分。关键是,对于两个进程,它不一定是相同的地址。因此,传递一个指针然后在另一个进程中执行这样的函数可能会“让你的机器着火(TM)”。

虚拟成员函数仍然与非虚拟成员函数几乎相同,例如“在执行时跳转到内存中的某个地址并将 this 指针传递给它”,但它是通过虚拟函数表(vtable)调用的的直接。因此,指向虚成员函数的指针几乎只是对象虚函数表的索引。调用该函数然后按照'获取对象指针,可能会增加指针以到达对象的 vtable 并跳转到该表的给定索引处的地址,将对象本身的地址作为this指针传递'。

免责声明:我有点倾向于“我真的知道我在说什么”——这里的舒适区。因此,如果我过分简化了某些事情或更糟,但从事散布虚假信息的活动,请随时将我的答案撕碎;)。

于 2013-02-28T03:33:10.803 回答
0

指针将始终作为虚拟内存存在,因此它是正确的,您可以检查地址并看到没有指向内存地址的物理指针

因为指向虚拟成员(本例中为 s)的指针是一种偏移量,所以它不依赖于对象在内存中的位置。因此,只要在两个地址空间中使用相同的对象布局,指向虚拟成员的指针就可以安全地在不同地址空间之间传递。与指向普通函数的指针一样,指向非虚拟成员函数的指针不能在地址空间之间交换。

于 2013-03-16T20:01:08.987 回答