临时对象的地址在C++中是否始终与其将要分配给的对象的地址相同?

huangapple go评论62阅读模式
英文:

Is the address of a temporary object always the same as the address of the object it will be assigned to in C++?

问题

In C++,假设我有一个名为A的C++类,在其中定义了一个变量A* ptr;。在构造函数中,有一条指令ptr = this。现在,让我们考虑以下的赋值初始化:A a = A()。我的主要问题是a.ptr == &a是否始终成立。

我的主要关注点与此赋值相关,首先创建一个临时对象,然后使用移动构造函数(可能是复制构造函数,我不完全确定)。我想了解临时对象中的ptr成员的值是否始终与将要分配给它的对象的地址相同(即临时对象的地址是否始终与a的地址相同)。

我进行了实验,似乎证实了这种行为,但我想知道这个特性是否由C++标准保证。

英文:

In C++, suppose I have a C++ class called A, and inside it, I define a variable A* ptr;. In the constructor, there is an instruction ptr = this. Now, let's consider the following assignment initialization: A a = A(). My main question is whether a.ptr == &a is always true.

My primary concern is related to this assignment, which first creates a temporary object and then uses either the move constructor (possibly the copy constructor, I'm not entirely sure). I want to understand if the value of the ptr member in the temporary object is always the same as the address of the object it will be assigned to (i.e., whether the address of the temporary object is always the same as the address of a).

I have conducted experiments that seem to confirm this behavior, but I would like to know if this feature is guaranteed by the C++ standard.

答案1

得分: 3

Assignment 在你已经构造了两个对象并且想要用一个对象的值覆盖另一个对象时发生。如果一个对象正在被构造,那就被称为 初始化,并且不会对该对象进行赋值。

A a;            // 初始化
A b(32);        // 初始化
A c = b;        // 初始化
b = c;          // 赋值
A d = A();      // 初始化
A e{42};        // 初始化
A *p = nullptr; // 初始化(指针的初始化)
p = new A();    // 对 p 进行赋值,初始化新的 A 对象
*p = d;         // 对 A 对象进行赋值

在你的情况下,A a = A(); 这一行执行初始化,因为此时正在构造 a。从C++17开始,只要表达式是 prvalue(比如你的 A()),初始化就会原地进行。这里不会创建临时对象,然后将其复制到其他地方的内存中,而是直接在正确的位置创建。

在这种情况下,你可以等效地写成 A a;,并且得到相同的效果。

英文:

Assignment happens when you have already constructed two objects and you want to overwrite one with the value of the other. If an object is being constructed right now, that's called initialization, and no assignment takes place to that object.

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

In your case the line A a = A(); performs initialization since a is being constructed right now. Since C++17, the initialization is performed in-place whenever the expression is a prvalue, like your A(). No copy is being performed here. The temporary object created by A() is never materialized in memory elsewhere and then copied, it's directly created in the right spot.

Effectively, you can pretend that no temporary object exists in this case.
You can equivalently write A a; and obtain the same effect.

答案2

得分: 0

以下是您要翻译的内容:

Next code shows the temp object, a named object and a copy of a temp object as a function parameter (pass by const reference). Both the temp object as the named object show the same addresses, but the copied temp object shows the new address in ptr, but the old address of the named object.

结构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

英文:

Next code shows the temp object, a named object and a copy of a temp object as a function parameter (pass by const reference). Both the temp object as the named object show the same addresses, but the copied temp object shows the new address in ptr, but the old address of the named object.

struct A
{
  A* a;
  A() : a(this) {}
};

void fnc(const A&amp; a)
{
  std::cout &lt;&lt; &quot;a.ptr &quot; &lt;&lt; a.a &lt;&lt; &quot; =?= &amp;a &quot; &lt;&lt; &amp;a &lt;&lt; &quot;\n&quot;;
}

int main()
{
  fnc(A());
  A a;
  fnc(a);
  a = A();
  fnc(a);
}

possible result

a.ptr 000000743053F7D8 =?= &amp;a 000000743053F7D8
a.ptr 000000743053F6F8 =?= &amp;a 000000743053F6F8
a.ptr 000000743053F7F8 =?= &amp;a 000000743053F6F8

答案3

得分: -2

总结你的问题,我们有以下内容:

class A {
    A() {
        ptr = this;
    }
    A* ptr;
}

int main() {
    A a = A();
}

正如你所述,A() 语句生成一个临时对象。= 运算符将调用复制构造函数。这执行的是浅复制,这意味着指针被复制,但不包括指针指向的内容。在这种情况下,这会导致未定义的行为。恰巧你的编译器很聪明,直接重用了临时对象,但据我所知,这不被语言保证。当管理指针时,你的类应该遵循三五零法则

在你的具体情况下,我建议正确地指定复制构造函数,以更新ptr为新的this地址。虽然我想你可能是在一个包含子对象的更广泛的程序中提出这个问题,所以在你的情况下可能不那么容易实现。

A(const A& other) {
    ptr = this;
}

根据规则,你应该类似地定义或删除所有相关操作符(移动、构造函数、析构函数)。

编辑:如果使用C++17或更高版本,复制省略是有保证的。请参考463035818-is-not-a-number的评论。因此,如果使用该版本或更高版本,你的原始代码是安全的。

英文:

To sum up your question, we have the following:

class A {
    A() {
        ptr = this;
    }
    A* ptr;
}

int main() {
    A a = A();
}

As you stated, the A() statement produces a temporary object. The = operator will call the copy constructor. This does a shallow copy which means pointers are copied but not the pointees. In that case, this produces undefined behavior. It happens that your compiler is smart and directly reuses the temporary object, but it is not guaranteed by the language to my knowledge. When managing pointers, your class should follow the rule of three/five/zero.

In your specific case, I suggest properly specifying the copy constructor to update ptr to the new this address. Although I suppose you are asking this question within a broader program with child objects so it may not be as trivial to implement in your case.

A(const A&amp; other) {
    ptr = this;
}

Following the rule, you should define or delete all related operators (move, constructors, destructor) simlarly.

Edit: Copy elision is guaranteed if using C++17 or above. See comment by 463035818-is-not-a-number . So your original code is safe with that version and up.

huangapple
  • 本文由 发表于 2023年6月12日 20:02:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76456472.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定