编辑对象通过引用确保函数返回后堆栈上的对象不被销毁吗?

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

Does editing objects by reference ensure that objects on the stack are not destroyed after the function returns?

问题

我无法理解以下代码的输出:

#include<iostream>
using namespace std;
template<typename T>
class foo{
    public:
        T a;
        foo(T b){
            this->a = b;
        }
        void print(){
            cout << a << endl;
        }
};
void goo(foo<int>& out){
    foo<int> a(10);
    out = a;
};
int main(){
    foo<int> a(5);    
    a.print();
    goo(a);
    a.print();
    return 0;
}
// 输出:(C++17 g++-13)
// 5
// 10

我想创建一个函数,它创建一个新对象并返回对它的引用,以便我可以稍后在其他项目中编辑该对象。但我发现在C++中返回引用是不安全的。因此,我想创建一个以引用作为参数的void函数,我会在函数中创建一个对象,然后编辑该引用。这似乎有效,但不应该有效,因为在函数返回后,新创建的对象应该被释放,并且引用现在应该指向一个未分配的地址。访问它应该会导致分段错误。但我并没有遇到任何此类错误。

英文:

I'm unable to understand the output of the following code

#include&lt;iostream&gt;
using namespace std;
template&lt;typename T&gt;
class foo{
    public:
        T a;
        foo(T b){
            this-&gt;a = b;
        }
        void print(){
            cout &lt;&lt; a &lt;&lt; endl;
        }
};
void goo(foo&lt;int&gt;&amp; out){
    foo&lt;int&gt; a(10);
    out = a;
};
int main(){
    foo&lt;int&gt; a(5);    
    a.print();
    goo(a);
    a.print();
    return 0;
}
// Output: (C++17 g++-13)
// 5
// 10

I wanted to create a function which create a new object and returns a reference to it so that I can edit that object later for some other project. But I found out that returning references is not safe in C++. So I thought of making a void function which takes in as an argument a reference, I would create an object in the function and then edit the reference. This seems to work, but it shouldn't as the new object that gets created should be collapsed after the function returns and the reference should now be pointing to a non-allocated address. Accessing it should give a segmentation fault. But I'm not getting any such faults.

答案1

得分: 6

将引用赋值不会重新绑定引用。你不能重新绑定引用。将引用赋值等同于对原始对象赋值。


> 我想创建一个函数,它创建一个新对象并返回对它的引用,以便我以后可以在其他项目中编辑该对象。

这不是你的代码所做的。而且这是好的。因为在函数返回后,你不能再使用对函数局部对象的引用,该对象已经不存在了。这将导致未定义的行为。


通过一个更简单的例子可能更容易理解。这就是你的函数所做的事情:

void goo(int& a) {    // a 是对某个 int 的引用
    int x = 42;        // x 是函数内部的局部变量
    a = x;             // "某个 int" 现在等于 42
}                     // x 的生命周期在此结束!

你担心的是,但你的代码没有做的是:

int& broken_code() { 
    int x = 42;       // x 是函数内部的局部变量
    return x;         // 返回对 x 的引用
}                    // x 的生命周期在此结束!

因为 x 是函数内部的局部变量,所返回的引用是悬垂引用。它不能在不引发未定义行为的情况下使用。

然而,在你的代码中,main 中的 foo<int> a(5);main 内的一个 foo<int> 对象。你将一个引用传递给 goo 并对该引用进行赋值。这等同于直接对 a 进行赋值。你并没有从函数中返回引用。


最后但并非最不重要的...

> [...] 访问它应该导致分段错误。

这是一个误解。虽然是一个常见的误解,但却是非常危险的。错误的代码,具有未定义行为的代码,不能保证会崩溃。当你的代码具有未定义行为时,实际上任何事情都有可能发生。期望崩溃是一个危险的误解,因为它让你相信要么代码崩溃,要么代码完全正常,这通常是错误的。为了确保你的代码没有未定义行为,有静态或运行时分析工具可用,但最终你需要确保代码执行了它应该执行的操作。

英文:

Assigning to a reference does not rebind the reference. You cannot rebind a reference. Assigning to a reference assigns to the original object.


> I wanted to create a function which create a new object and returns a reference to it so that I can edit that object later for some other project.

Thats not what your code does. And thats good. Because you cannot use a reference to a function local object after the function returned and the objec is gone. That would result in undefined behavior.


This is perhaps better understood by means of a simpler example. This is what your function does:

 void goo(int&amp; a) {    // a is a reference to some int
    int x = 42;        // x is local to the function
    a = x;             // &quot;some int&quot; now equals 42
 }                     // x lifetime ends here !

What you are afraid of, but what your code is not doing is the following:

 int&amp; broken_code() { 
    int x = 42;       // x is local to the function
    return x;         // return a reference to x
 }                    // x lifetime ends here !

Because x is local to the function, the reference returned is a dangling reference. It cannot be used without invoking undefined behavior.

In your code however, foo&lt;int&gt; a(5); in main is a foo&lt;int&gt; local to main. You do pass a reference to goo and assign something to that reference. This is equivalent to assigning directly to a. You do not return a reference from the function.


Last but not least...

> [...] Accessing it should give a segmentation fault.

Thats a misunderstanding. A common one, though a very dangerous one. Wrong code, code that has undefined behavior, is not guaranteed to crash. When your code has undefined behavior then literally anything is possible to happen. Expecting a crash is a dangerous misconception, because it makes you believe that either the code crashes or it is totally fine, which is generally wrong. To make sure your code is free of undefined behavior there are tools for static or runtime analysis, but eventually it is up to you to make sure that the code does what it should do.

答案2

得分: -1

当你将对象作为引用传递时,你可以编辑在创建对象时存储在对象地址上的值。
在函数中创建一个新对象,其值为10。它在另一个地址上创建,但当你将其分配给引用变量时,它会被复制到其中。
这是在使用静态分配内存时的情况,但在动态分配内存的情况下,如果像普通变量一样复制了新对象的分配内存,那么该内存将被删除。这被称为浅拷贝。
在这种情况下,你将不得不进行深拷贝。

更多信息,请参考C++中的浅拷贝和深拷贝

英文:

When you are passing the object as reference you can edit the values stored at the address of object when it was created.
You are creating a new object in the function with value 10. It is created at another address but when you assign it to the referenced variable, it gets copied to it.
This is the case when you are using statically allocated memory, But in the case of dynamically allocated memory the new object's allocated memory would have deleted if you have copied it like normal variable. It is known as shallow copy.
In that case you would have to what is know as deep copy.

For more information you can refer Shallow Copy and Deep Copy in C++

huangapple
  • 本文由 发表于 2023年7月3日 16:16:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76602971.html
匿名

发表评论

匿名网友

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

确定