2

このコードは未定義の動作を引き起こしますか? または、これで問題が発生することはありますか? (関数なしで完全なクラスをコピーし、 public 修飾子を持つ変数だけをコピーし、このポインターをスローするプライベートメンバーを変更します) 例:

#include <iostream>

using namespace std;

class Point {

private:

    int x;
    int y;

public:

    Point(int x, int y) {
        this->x = x;
        this->y = y;
    }

    void Print() {
        cout << "(" << x << ", " << y << ")" << endl;
    }

};

struct PointHack {

    int x;
    int y;

};

int main() {
    Point a(4, 5);
    a.Print();
    ((PointHack *) (&a))->x = 1;
    ((PointHack *) (&a))->y = 2;
    a.Print();

    return 0;
}

出力:

(4, 5)
(1, 2)

(もちろんオリジナルメンバーオーダーで)

4

3 に答える 3

5

クラスはレイアウト互換性がありますが (以下を参照)、コードは未定義の動作を示します。これは、そのようなポインター キャストがC++ の厳密なエイリアシング ルール1によって禁止されているためです。

ただし、キャストを a に置き換えるとunion、コードが標準に準拠します。これは実際に C++11 で動作することが保証されています。

#include <iostream>

using namespace std;

class Point {

private:

    int x;
    int y;

public:

    Point(int x, int y) {
        this->x = x;
        this->y = y;
    }

    void Print() {
        cout << "(" << x << ", " << y << ")" << endl;
    }

};

struct PointHack {
    int x;
    int y;
};

union pu
{
    Point p;
    PointHack ph;
    pu(int x, int y) : p(x, y) {}
};

int main() {
    pu u(4,5);
    u.p.Print();
    u.ph.x=1;
    u.ph.y=2;
    u.p.Print();
    return 0;
}

これは、PointPointHackが標準レイアウト クラス2 (C++11、§9 ¶7) であり、「共通の初期サブシーケンス」 (§9.2、¶20) を共有しているという事実に由来します。そのため、両方が同じ共用体 (ここではpu) に格納されている場合、「それらの共通の最初の部分を検査する」ことが許可されています3

それでも、この答えは主にスタイルの練習です。本当に強制されない限り、そのようなトリックを悪用しないでください。C++ は、カプセル化を容赦なく破ることなく、必要に応じてプライベート メンバーにアクセスするためのより良い手段を提供します。ゲッター/セッター、保護された継承、フレンド クラスなどがあります。そのクラスのデータがどのように変更されるかについてのそのクラスの仮定に違反している可能性があり、そのメソッドのコードの不安定な動作につながる可能性があります。


ノート:

  1. C++ では、同じオブジェクトを指す無関係な型の 2 つのポインターを持つことはできません。この制限は主に、オプティマイザーがエイリアシングに関する想定を支援するために使用されます。
  2. この要件は非常に厳しいことに注意してください。通常、基本的に C 構造体ではないほとんどのクラスは、これに該当しません。アクセス修飾子が異なっていても、魔法が壊れる可能性があります。
  3. に代入phすると の「アクティブなオブジェクト」になりunionp.Print()「非アクティブな」オブジェクトを「検査」するという考え方です。
于 2013-10-06T14:23:11.697 に答える
2

はい、投稿されたコードは未定義の動作を呼び出します。そうしないでください。

一部の人々がこの不快な目標を達成する本当に正気でない方法を見たい場合は、ここに行きます:テンプレート トリックを使用してプライベート メンバーにアクセスします。

しかし、実際にはそうしないでください。

PS: C++ で C スタイルのキャストを使用しないでください。必要に応じて、static_cast、dynamic_cast、reinterpret_cast、および const_cast を使用します。C スタイルのキャストは不必要に安全ではありません。

于 2013-10-06T13:46:39.233 に答える
0

私は問題を見つけました

/*code:*/
#include <stdio.h>

void check(short *h,long *k)
{
    *h=5;
    *k=6;
    if (*h == 5)
        printf("strict aliasing problem\n");
}

int main(void)
{
    long      k[1];
    check((short *)k,k);
    return 0;
}
/*
$ gcc -Wall -ansi -pedantic -O0 -o o0 asd.c
$ ./o0
/output: nothing/
$ gcc -Wall -ansi -pedantic -O2 -o o2 asd.c
$ ./o2
/output: strict aliasing problem/
*/

(C++ と同じ)

于 2013-10-07T11:19:52.147 に答える