我正试图在指针的帮助下访问私有成员。我想知道为什么我们在(int *)& t前面提到dtype?
class Test
{
private:
int data;
public:
Test() { data = 0; }
int getData() { return data; }
};
int main()
{
Test t;
int* ptr = (int*)&t;
*ptr = 10;
cout << t.getData();
return 0;
}
答案 0 :(得分:3)
门禁系统适用于名称。名称data
是私有的,不是任何变量或关联的存储区域。换句话说,只要您不使用名称data
,就可以通过其他方式访问变量。
访问控制的目的是防止代码意外中断encapsulation。
有时候说C ++“给你足够的绳索来吊自己”,或“防止墨菲而不是Macchiavelli” - 换句话说,如果你真的想要,你可以绕过访问控制。 Link to related article - GotW #76
当然,您应该尝试以不需要绕过访问控制的方式设计代码。
在这种特殊情况下,代码是明确定义的。由于Test
是standard layout class,因此保证在第一个数据成员之前没有填充,并且also guaranteed此强制转换生成指向t.data
的指针,可用于访问所述变量。
对于更复杂的类,如果有问题的数据成员没有从对象的存储空间的开头开始,则代码可能无效。
答案 1 :(得分:0)
(int*)&t
:将指针强制转换为指向整数
int* ptr = (int*)&t;
:它将t的指针存储在ptr
答案 2 :(得分:0)
它只是偶然的机会开始。
Test
是一个原始类,没有涉及继承,没有虚拟方法,没有RTTI支持编译,或者任何其他可能会破坏内存表示的东西。
在这个简单的例子中,唯一的成员变量与整个对象位于同一地址。这是非常不安全的依赖,因为它是任何更复杂的未定义行为。
尝试例如现在将继承和虚拟方法带入您的测试中。突然,对象的前几个字节不再是第一个成员变量(编译器通常放置VPTR的位置),并且您可以测试崩溃的可能性。
或者让你的成员变量变为静态,现在你的对象本身实际上是空的,你可以访问堆控制结构,这不一定会立即崩溃,而是更糟糕的堆损坏。好吧,在这种情况下t
是堆栈分配的,所以你只会破坏你的堆栈(这稍微差一些),但如果它是堆分配的,这将变得至关重要。