为什么我们必须在对象的地址前面提到访问C++中类的私有成员的数据类型?
我试图通过指针访问私人成员。我想知道为什么我们在(int *)& t前提到dtype?为什么我们必须在对象的地址前面提到访问C++中类的私有成员的数据类型?
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;
}
(int*)&t
:投射指针是指向整数
int* ptr = (int*)&t;
:储存吨的指针在PTR
的访问控制系统适用于名称。它的名称是data
,它是私有的,没有任何变量或关联的存储区域。换句话说,只要您不使用名称data
这样做,您可以用其他方式访问该变量。
访问控制的目的是防止代码意外中断encapsulation。
有时候会说C++“给你足够的绳索来挂你自己”,或者“防止墨菲而不是Macchiavelli” - 换句话说,如果你真的想要,你可以绕过访问控制。 Link to related article - GotW #76
当然,你应该尝试设计你的代码,使你不需要绕过访问控制。
在这种特殊情况下,代码是明确的。由于Test
是standard layout class,因此可以确保在第一个数据成员之前没有填充,并且also guaranteed该演员生成可用于访问所述变量的指针t.data
。
对于更复杂的类,如果有问题的数据成员未在对象的存储空间的起始位置开始,代码可能无法正常工作。
它只能偶然地起作用。
Test
是一个没有涉及继承的原始类,没有虚拟方法,没有RTTI支持编译,或其他任何可能会膨胀内存表示的东西。
在这个简单的例子中,唯一的成员变量与整个对象位于相同的地址。这是非常不安全的依赖,因为它对于任何更复杂的事情都是未定义的行为。
尝试例如现在将继承和虚拟方法引入您的测试。突然你的对象的头几个字节不再是第一个成员变量(这是编译器通常放置VPTR的地方),而且你测试了可怕的崩溃。
或者让你的成员变量为静态的,而现在你的对象本身实际上是空的,而你访问堆控制结构,而不一定立即崩溃,但堆腐败更糟糕。那么,在这种情况下,t
是堆栈分配的,所以你只会损坏你的堆栈(这稍微差一些),但是如果是堆分配,这将变得至关重要。
这是没有意义的:*** int * ptr =(int *)&t; *** –
如果您需要直接访问该值,请将'data'设为公共。你可以为你的类创建访问和操作数据的方法。你应该避免直接操作自己范围之外的类的成员。 –