编译器如何控制变量在内存中的保护?私有变量是否与标签位相关联?它是如何工作的?
编译器如何控制变量在内存中的保护?私有变量是否与标签位相关联?它是如何工作的?
private
成员,那么在运行时没有任何保护措施。所有的保护都是在编译时进行的,如果你知道它们在内存中的布局方式,就可以始终访问一个类的私有成员。这需要平台和编译器的知识,并且在某些情况下甚至可能取决于编译器设置,如优化级别。#include <iostream>
class FourChars {
private:
char a, b, c, d;
public:
FourChars(char a_, char b_, char c_, char d_)
: a(a_), b(b_), c(c_), d(d_)
{
}
};
int main()
{
FourChars fc('h', 'a', 'c', 'k');
char const *p = static_cast<char const *>(static_cast<const void *>(&fc));
std::cout << p[0] << p[1] << p[2] << p[3] << std::endl;
}
这个复杂的转换是因为void*
是任何指针可以强制转换成的唯一类型。然后void*
可以被转换成char*
而不违反严格别名规则。也可能只需要使用一个reinterpret_cast
,但实际上我从不使用这种“肮脏的把戏”,所以对于如何以最快的方式执行它们并不太熟悉 :)
FourChars
必须至少有四个大小,打印其前四个字节的结果具有实现相关,但不是未定义的行为。 - Fred Fooreinterpret_cast<>
? - Vlad编译器的工作是确保某些成员是私有的,并阻止您使用它们。在编译后,它们与其他成员没有太大区别。
然而,重要的一点是数据成员不需要按照它们在类定义中出现的顺序在内存中布局,但对于访问级别相同的变量,必须按照顺序进行布局。
&b > &a
,(&a)[1]
不一定是b
)。但一旦给出编译器并定义编译选项,行为就不取决于执行。如果它能工作,总是能工作;如果不能......永远不会。事实上,这是非可移植代码。 - Emilio Garavaglia