下面的段落摘自Stroustup的书"The C++ Programming Language"(第三版)第420页:
因为指向虚拟成员的指针(在此示例中为s)是一种偏移量,所以它不依赖于对象在内存中的位置.因此,只要在两者中使用相同的对象布局,就可以安全地在不同的地址空间之间传递指向虚拟成员的指针.与普通函数的指针一样,指向非虚拟成员函数的指针不能在地址空间之间交换.
我在争论本段最后一句话.下面,您将找到一个代码片段,其中指向非虚拟成员函数的指针,foo()以及foo1()在一个基础对象a和派生对象之间交换b,没有问题.
不能做的是重载,基类中的任何函数,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)();
}
Run Code Online (Sandbox Code Playgroud)
这句话是正确的:在(标准)C++ 中,一个程序,或者更确切地说,进程,只有一个地址空间。因此,正如 ulidtko 指出的那样,这句话指的是在不同进程的地址空间之间交换指向虚拟成员函数和非虚拟成员函数的指针的可能性。
类的非虚拟成员函数几乎是一个标准函数,带有您调用它的对象(this指针)的隐式参数。因此,它在加载时会在进程的地址空间中分配一些地址。它在地址空间中的确切位置肯定取决于您的平台以及该成员函数是否是动态链接库的一部分。关键是,对于两个进程来说,它不一定是相同的地址。因此,传递一个指针,然后在另一个进程中执行这样的函数可能会“让你的机器着火(TM)”。
虚拟成员函数仍然与非虚拟成员函数几乎相同,如“在执行时跳转到内存中的某个地址并将其传递给 this 指针”,但它是通过虚拟函数表(vtable)调用的的直接。因此,指向虚拟成员函数的指针几乎只是对象虚拟函数表的索引。调用该函数会执行类似“获取对象指针,可能会增加指针以到达对象的 vtable 并跳转到该表的给定索引处的地址,将对象本身的地址作为 this 指针传递”的操作'。因此,通过 vtable 的这种间接寻址使得在地址空间之间交换指向虚拟成员函数的指针成为可能。
免责声明:我有点倾向于我的“我真的知道我在说什么”——这里的舒适区。因此,如果我过于简单化了某些事情或更糟,但参与了散布虚假信息,请随意撕碎我的答案;)。
| 归档时间: |
|
| 查看次数: |
235 次 |
| 最近记录: |