31

指针不就是地址吗?或者我错过了什么?

我测试了几种类型的指针:

  • 指向任何变量的指针都是相同的(在我的平台上为 8B)
  • 指向函数的指针与指向变量的指针大小相同(再次为 8B)
  • 指向具有不同参数的函数的指针 - 仍然相同 (8B)

但是指向成员函数的指针更大 - 在我的平台上为 16B。

三件事:

  1. 为什么指向成员函数的指针更大?他们还需要什么信息?
  2. 据我所知,该标准没有说明指针的大小,只是void*必须能够“包含”任何指针类型。换句话说,任何指针都必须能够转换为void*,对吧?如果是这样,那么为什么sizeof( void* )是 8,而sizeof指向成员函数的指针是 16?
  3. 是否还有其他大小不同的指针示例(我的意思是,对于标准平台,而不是一些稀有和特殊的)?
4

5 回答 5

32

在最正常的情况下,你几乎可以想到

struct A {
    int i;
    int foo() { return i; }
};

A a;
a.foo();

作为

struct A {
    int i;
};
int A_foo( A* this ) { return this->i; };

A a;
A_foo(&a);

(开始看起来像C,对吗?)所以您会认为指针&A::foo与普通函数指针相同。但是有一些复杂性:多重继承和虚函数。

所以想象我们有:

struct A {int a;};
struct B {int b;};
struct C : A, B {int c;};

它可能是这样布置的:

多重继承

如您所见,如果您想用 anA*或 a指向对象C*,则指向起点,但如果您想用 a 指向它,B*则必须指向中间的某个位置。

因此,如果C从某个成员函数继承B并且您想指向它然后在 a 上调​​用该函数C*,则它需要知道对this指针进行洗牌。该信息需要存储在某个地方。所以它与函数指针混为一谈。

现在,对于每个具有virtual函数的类,编译器都会创建一个名为virtual table的列表。然后,它将指向该表的额外指针添加到类(vptr)。所以对于这个类结构:

struct A
{
    int a;
    virtual void foo(){};
};
struct B : A
{
    int b;
    virtual void foo(){};
    virtual void bar(){};
};

编译器最终可能会变成这样: 在此处输入图像描述

所以指向虚函数的成员函数指针实际上需要成为虚表的索引。所以成员函数指针实际上需要 1) 可能是函数指针,2) 可能是this指针的调整,以及 3) 可能是 vtable 索引。为了保持一致,每个成员函数指针都需要具备所有这些功能。所以这是8指针的4字节,调整的4字节,索引的字节,16字节总数。

我相信这实际上在编译器之间存在很大差异,并且有很多可能的优化。可能没有人真正按照我描述的方式实现它。

有关更多详细信息,请参阅(滚动到“成员函数指针的实现”)。

于 2012-08-17T13:35:53.420 回答
7

基本上是因为它们需要支持多态行为。请参阅 Raymond Chen 的一篇不错的文章

于 2012-08-17T13:37:28.927 回答
2

一些解释可以在这里找到: 成员函数指针的底层表示

尽管指向成员的指针表现得像普通指针,但在幕后它们的表示是完全不同的。实际上,在某些情况下,指向成员的指针通常由一个最多包含四个字段的结构组成。这是因为指向成员的指针不仅要支持普通的成员函数,还要支持虚成员函数、具有多个基类的对象的成员函数以及虚基类的成员函数。因此,最简单的成员函数可以表示为一组两个指针:一个保存成员函数的物理内存地址,第二个指针保存 this 指针。但是,在虚拟成员函数、多重继承和虚拟继承等情况下,指向成员的指针必须存储附加信息。因此,你可以' t 将指向成员的指针转换为普通指针,也不能在指向不同类型成员的指针之间安全地转换。块引用

于 2012-08-17T13:55:42.000 回答
0

我猜它与this指针有关......也就是说,每个成员函数还必须具有它们所在类的指针。然后指针使函数的大小更大一些。

于 2012-08-17T13:38:15.260 回答
0

将指向成员函数的指针表示为如下的一些主要原因{this, T (*f)()}

  • 它在编译器中的实现比实现指向成员函数的指针更简单T (*f)()

  • 它不涉及运行时代码生成或额外的簿记

  • T (*f)()

  • C++ 程序员对指向成员函数的指针的大小没有足够的要求等于sizeof(void*)

  • 在执行期间生成运行时代码实际上是当前 C++ 代码的一个禁忌

于 2016-08-12T10:33:46.757 回答