提问人:Zhang Yuhan 提问时间:6/12/2023 更新时间:6/12/2023 访问量:92
临时对象的地址是否始终与 C++ 中将分配给它的对象的地址相同?
Is the address of a temporary object always the same as the address of the object it will be assigned to in C++?
问:
在 C++ 中,假设我有一个名为 的 C++ 类,并在其中定义了一个变量。在构造函数中,有一个指令。现在,让我们考虑以下赋值初始化: .我的主要问题是是否总是正确的。A
A* ptr;
ptr = this
A a = A()
a.ptr == &a
我主要关心的是与此赋值有关,它首先创建一个临时对象,然后使用移动构造函数(可能是复制构造函数,我不完全确定)。我想知道临时对象中成员的值是否总是与它将被分配到的对象的地址相同(即,临时对象的地址是否总是与 的地址相同)。ptr
a
我进行了似乎证实了这种行为的实验,但我想知道 C++ 标准是否保证了此功能。
答:
总结您的问题,我们有以下几点:
class A {
A() {
ptr = this;
}
A* ptr;
}
int main() {
A a = A();
}
如前所述,该语句生成一个临时对象。运算符将调用复制构造函数。这执行浅层复制,这意味着指针被复制,但指针没有被复制。在这种情况下,这会产生未定义的行为。碰巧你的编译器很聪明,直接重用临时对象,但据我所知,语言并不能保证。在管理指针时,您的类应遵循 3/5/0 规则。A()
=
在您的特定情况下,我建议正确指定复制构造函数以更新到新地址。尽管我想您是在具有子对象的更广泛的程序中提出这个问题,因此在您的情况下实现它可能不是那么简单。ptr
this
A(const A& other) {
ptr = this;
}
按照该规则,您应该以类似的方式定义或删除所有相关运算符(move、构造函数、析构函数)。
编辑:如果使用 C++17 或更高版本,则保证复制省略。请参阅 463035818-is-not-a-number 的评论。因此,您的原始代码在该版本及更高版本中是安全的。
评论
A a = A();
A a;
A a = A();
A a = A();
A
a
当您已经构造了两个对象,并且想要用另一个对象的值覆盖另一个对象时,就会发生赋值。如果现在正在构造一个对象,则称为初始化,并且不会对该对象进行赋值。
A a; // initialization
A b(32); // initialization
A c = b; // initialization
b = c; // assignment
A d = A(); // initialization
A e{42}; // initialization
A *p = nullptr; // initialization (of a pointer)
p = new A(); // assignment of p, initialization of the new A object
*p = d; // assignment of the A object
在您的情况下,该行执行初始化,因为现在正在构建中。从 C++17 开始,只要表达式是 prvalue,就会就地执行初始化,比如你的 .此处不执行任何复制。创建的临时对象永远不会在其他地方的内存中具体化,然后复制,而是直接在正确的位置创建。A a = A();
a
A()
A()
实际上,在这种情况下,您可以假装不存在临时对象。
您可以等效地编写并获得相同的效果。A a;
接下来的代码显示 temp 对象、命名对象和 temp 对象的副本作为函数参数(通过 const 引用传递)。作为命名对象的 temp 对象都显示相同的地址,但复制的临时对象在 ptr 中显示新地址,但显示命名对象的旧地址。
struct A
{
A* a;
A() : a(this) {}
};
void fnc(const A& a)
{
std::cout << "a.ptr " << a.a << " =?= &a " << &a << "\n";
}
int main()
{
fnc(A());
A a;
fnc(a);
a = A();
fnc(a);
}
可能的结果
a.ptr 000000743053F7D8 =?= &a 000000743053F7D8
a.ptr 000000743053F6F8 =?= &a 000000743053F6F8
a.ptr 000000743053F7F8 =?= &a 000000743053F6F8
评论
A a = A()
=