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

C++ - Change private member from outside the class

本文关键字:成员 C++      更新时间:2023-10-16

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

#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)

(当然是原始会员订单(

尽管您的类与布局兼容(见下文(,但由于C++严格的别名规则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()是"检查"非活动"对象的对象。

是的,发布的代码调用未定义的行为。 别这样。

如果你想看到一些人实现这个令人讨厌的目标的疯狂方式,你可以去: 使用模板技巧访问私人成员

但实际上,不要那样做。

PS:切勿在C++中使用C型转换。 根据需要使用static_cast、dynamic_cast、reinterpret_cast和const_cast。 C 型石膏不必要地不安全。

我发现了问题

/*code:*/
#include <stdio.h>
void check(short *h,long *k)
{
    *h=5;
    *k=6;
    if (*h == 5)
        printf("strict aliasing problemn");
}
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++ 相同(