关于此代码:
#include <string>
int main()
{
union u {
u() { i = 0; }
~u() {}
int i;
std::string s1;
std::string s2;
} u;
new (&u) std::string{};
}
对象可以包含其他对象,称为子对象。子对象可以是成员子对象([class.mem]),基类子对象([class.derived])或数组元素。不是任何其他对象的子对象的对象称为完整对象。 如果在与成员子对象或数组元素e(可能在其生命周期内或可能不在其寿命内)关联的存储器中创建对象,则在以下情况下,创建的对象是e包含对象的子对象:
— e的包含对象的生存期已经开始并且没有结束,并且
—新对象的存储完全覆盖了与e关联的存储位置,并且
—新对象与e 具有相同的类型(忽略cv限定)。
不需要如何在与成员子对象关联的存储中创建对象。如果子对象是标准布局联合的成员或非联盟类对象的第一个成员,则代码不必在address-of运算符的参数中指定子对象。在这种情况下,只要获取包含对象的地址来指定成员子对象的存储即可。
«没有要求如何创建对象»,除其他外,这意味着赋予放置new的指针不必point指向子对象。主要是因为可能没有指向的对象(请注意,[intro.object]/2不需要子对象处于活动状态)。在标准讨论邮件列表中,有人问给定类型x
的对象struct A { unsigned char buf[1]; };
,new (&x) A{}
和new (x.buf) A{}
之间有区别吗?而且,在两种情况下,the answer都不是,x.buf
对于A{}
将是provide storage。因为
[intro.object]和[basic.life]中的用语与指针所代表的存储地址有关,而不与指针所指向的对象有关。
[class.union]/1发誓«联合类型对象的最多一个非静态数据成员可以随时处于活动状态”。
上面的代码s1
或s2
中,哪个是活跃的?
答案 0 :(得分:7)
指针是一个地址,但是对于对象模型,它不仅仅是一个地址。它指向该地址处的特定对象。某个地址上可以存在多个对象,但这并不意味着指向这些对象中任何一个的指针是同时指向该地址上其他对象的指针。考虑[expr.unary.op] / 1关于指针间接寻址的说法:
结果是一个左值,表示表达式所指向的对象或函数。
不要“在那个地址有物体”;它是一个左值,表示所指向的对象。显然,在C ++对象模型中,同一地址可以存在多个对象,但是指向该地址的特定指针并不指向所有这些对象。它仅指向其中之一。
[expr.unary.op] / 2说:“一元&运算符的结果是指向其操作数的指针”。因此,&u
指向类型为u
的{{1}}(顺便说一句,是否真的有必要将对象命名为相同的类型?)。 u
并不指向&u
,u.i
或u.s1
。保证所有这些地址都与u.s2
共享相同的地址,但是&u
本身仅指向&u
。
现在的问题是,u
代表什么存储空间?好吧,根据[intro.object] / 1,我们知道“一个对象占用了一个存储区域”。如果&u
指向对象&u
,则该指针必须代表该对象占用的存储区域。不存储其任何子对象;它是该对象的存储。全部。
现在,我们进入u
。该表达式在由new(&u) std::string{}
表示的存储中创建类型为std::string{}
的对象。这表示重新使用对象&u
的存储。符合[basic.life] /1.4,终止u
的生存期。终止其活动成员子对象的生存期。
因此,您的问题的答案是两个都不会激活,因为对象u
不再存在。