我知道以下情况很糟糕,但我的印象是,班级的第一个成员是班级的起始地址。这是错的吗?
class A{
public:
int a;
int b;
};
class B{
public :
int x;
};
int main()
{
B *pb=new B();
A *pa=(A*)pb;
pa->a++;
}
我的印象是pb-> x会增加1.它总是真的还是未定义的?当我们有用户定义的构造函数或虚函数时,为什么会改变?
答案 0 :(得分:4)
仅当您的类属于standard_layout类型时才会出现这种情况。您可以使用类型特征is_standard_layout
进行测试 std::cout << std::is_standard_layout<A>::value << '\n';
std::cout << std::is_standard_layout<B>::value << '\n';
对于其他类,您可以在内存中存储其他信息,这些信息是特定于编译器的,而不是标准化的。您可以查看This question,其中讨论并显示了一些内存布局。
对于您的第二个示例/问题,该标准具有以下引用(5.2.10 / 7,N3337草案):
可以将对象指针显式转换为不同类型的对象指针。当“指向T1的指针”类型的prvalue v转换为“指向cv T2的指针”类型时, 结果是static_cast(static_cast(v))如果T1和T2都是标准布局类型(3.9)并且T2的对齐要求不比T1更严格, 或者如果任何一种类型无效。将“指向T1的指针”的prvalue转换为“指向T2的指针”类型(其中T1和T2是对象类型,T2的对齐要求是 没有比T1更严格的并且返回其原始类型产生原始指针值。任何其他此类指针转换的结果都未指定。
如果我正确地阅读并解释了这个,那么你的例子是未指定的,因为A的对齐要求比B的要大。但是,另一种方式应该没问题,例如:
int main()
{
A *pa=new A();
B *pb=reinterpret_cast<B*>(pa);
pb->x++;
std::cout << pa->a;
}
答案 1 :(得分:2)
您通过将指针强制转换为另一个类来违反严格别名。如果您的类是标准布局,则只能将其强制转换为第一个成员的类型。
答案 2 :(得分:-1)
是的,你是绝对错误的,你永远不能认为这是理所当然的,这些是C ++不保证的编译器特定合同。
我相信你把类与C ++数组混淆了:例如,指向char数组的第一个成员的指针与指向该数组的指针相同。