C ++ - 从类外部更改私有成员

时间:2013-10-06 13:43:59

标签: c++

此代码是否导致未定义的行为?或者我可以遇到这个问题? (复制没有函数的完整类,只使用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)

(当然是原始会员订单)

3 个答案:

答案 0 :(得分:3)

尽管您的类与布局兼容(请参阅下文),但由于the C++ strict aliasing rules 1 禁止此类指针转换,您的代码会显示未定义的行为。

但是:用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. 我的想法是,分配给ph会使其成为union的“活动对象”,然后p.Print()就是“检查”“非活动”对象。

答案 1 :(得分:1)

是的,发布的代码会调用未定义的行为。不要那样做。

如果你想看到某些人实现这个令人讨厌的目标的疯狂方式,请转到:access private member using template trick

但实际上,不要这样做。

P.S。:从不在C ++中使用C风格的演员表。根据需要使用static_cast,dynamic_cast,reinterpret_cast和const_cast。 C风格的演员阵容不必要地不安全。

答案 2 :(得分: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 ++相同)