此代码是否会导致未定义的行为?或者我可以遇到这个问题吗? (复制没有函数的完整类,只是带有 public 修饰符的变量并修改私有(private)成员抛出这个指针) 示例:
#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)
(当然是用原来的成员(member)顺序)
最佳答案
尽管您的类是布局兼容的(见下文),但由于 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;
}
这是因为 Point
和 PointHack
是标准布局类2(C++11,§9 ¶7) ,并共享一个“公共(public)初始子序列”(§9.2,¶20);因此,如果它们都存储在同一个 union 中(此处为 pu
),则允许“检查其中任何一个的公共(public)初始部分”3。
不过,这个答案主要是一种风格练习;除非你真的被迫,否则不要利用这些技巧。 C++ 提供了更好的方法来在必要时访问私有(private)成员,而不会残酷地破坏封装——你有 getter/setter、 protected 继承、友元类……而且通常,如果你以目标类不希望的方式访问私有(private)类成员,您可能违反了该类关于如何修改其数据的假设,这可能导致其方法代码的行为不稳定。
注释:
- 在C++中,你不能有两个不相关类型的指针指向同一个对象;此限制主要用于帮助优化器做出关于别名的假设。
- 请注意,这方面的要求非常严格;通常,大多数基本上不是 C 结构的类都不符合此条件。即使有不同的访问限定符也可以打破魔法。
- 思路是赋值给
ph
使其成为union
的“事件对象”,然后p.Print()
就是“检查”“非事件”对象的那个。
关于C++ - 从类外部更改私有(private)成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19209535/