2

此代码是否会导致未定义的行为?或者我可以遇到这个问题吗?(复制没有函数的完整类,只使用带有公共修饰符的变量并修改私有成员抛出这个指针)示例:

#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++ 提供了更好的方法来访问私有成员,而不会粗暴地破坏封装 - 你有 getter/setter、受保护的继承、友元类……一般来说,如果你以目标类不打算的方式访问私有类成员,您可能违反了该类关于如何修改其数据的假设,这可能导致其方法的代码行为不稳定。


笔记:

  1. 在 C++ 中,不能有两个不相关类型的指针指向同一个对象;此限制主要用于帮助优化器假设别名。
  2. 请注意,对此的要求非常严格;通常大多数基本上不是 C 结构的类都不符合此条件。即使有不同的访问限定符也可以打破魔法。
  3. 这个想法是分配 toph使其成为 的“活动对象” union,然后p.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 回答