2

Inside the C++ Object Model」では、0 へのポインターと最初のデータ メンバーへのポインターを区別するために、クラス内のデータ メンバーのオフセットは常に実際のオフセットよりも 1 大きいと述べています。次に例を示します。

class Point3d {
public:
     virtual ~Point3d();

public:
    static Point3d origin;
    float x, y, z;
};
//to be used after, ignore it for the first question
int main(void) {        
    /*cout << "&Point3d::x = " << &Point3d::x << endl;
    cout << "&Point3d::y = " << &Point3d::y << endl;
    cout << "&Point3d::z = " << &Point3d::z << endl;*/
    printf("&Point3d::x = %p\n", &Point3d::x);
    printf("&Point3d::y = %p\n", &Point3d::y);
    printf("&Point3d::z = %p\n", &Point3d::z);
    getchar();
}

したがって、以下の 2 つのポインターを区別するために、データ メンバーのオフセットは常に 1 つ多くなります。

float Point3d::*p1 = 0;
float Point3d::*p2 = &Point3d::x;

上記のメイン関数は、この引数を検証するためにメンバーのオフセットを取得しようとするもので、次のように出力されるはずです: 5、9、13 (先頭の 4 バイトの vptr を考慮してください)。ただし、 MS Visual Studio 2012では、出力は次のようになります。

&Point3d::x = 00000004
&Point3d::y = 00000008
&Point3d::z = 0000000C

質問: MS C++ コンパイラは、このメカニズムを防ぐために何らかの最適化または何かを行いましたか?

4

6 に答える 6

1

「特定の型のメンバーへのポインター」型の null ポインターは、その型の null 以外の値とは異なる必要がありますが、null 以外のポインターを 1 だけオフセットすることは、コンパイラがこれを保証できる唯一の方法ではありません。 . たとえば、私のコンパイラは、メンバーへの null ポインターの非ゼロ表現を使用しています。

namespace {
struct a {
    int x, y;
};
}

#include <iostream>

int main() {
    int a::*p = &a::x, a::*q = &a::y, a::*r = nullptr;

    std::cout << "sizeof(int a::*) = " << sizeof(int a::*)
              << ", sizeof(unsigned long) = " << sizeof(long);

    std::cout << "\n&a::x = " << *reinterpret_cast<long*>(&p)
              << "\n&a::y = " << *reinterpret_cast<long*>(&q)
              << "\nnullptr = " << *reinterpret_cast<long*>(&r)
              << '\n';
}

次の出力が生成されます。

sizeof(int a::*) = 8, sizeof(unsigned long) = 8
&a::x = 0
&a::y = 4
nullptr = -1

コンパイラは、同一ではないにしても、おそらく同様のことを行っています。このスキームは、null 以外のメンバーへのポインターを使用するたびに追加の「1 を減算」する必要がないため、実装のほとんどの「通常の」ユース ケースでおそらくより効率的です。

于 2013-04-27T16:35:15.213 に答える