提问者:小点点

Rust vs C++:从函数返回对象


我是Rust的新手,并试图理解当从函数返回对象时所有权是如何传递的。 在下面的基于引用的实现中,由于引用没有所有权,所以当“s”超出作用域时,它将被删除并释放。

fn dangle() -> &String { // dangle returns a reference to a String

    let s = String::from("hello"); // s is a new String

    &s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
  // Danger!

这是通过不返回引用来解决的:

fn no_dangle() -> String {
    let s = String::from("hello");

    s
}

现在我试图用C++实现来理解这一点,如下所示:

std::string no_dangle() {
    std::string s("hello world");
    return s;
}

根据我的理解,在C++中,当从函数返回“s”时,会使用复制构造函数创建另一个副本,而在函数内部创建的“s”会被释放,这意味着会创建两个对象,而这两个对象在内存方面并不是光学的。

我的问题:

>

  • 在Rust中,当从函数返回“s”时,不会创建额外的对象。只返回所有权。在堆中分配的原始对象保持不变。这样做对吗?

    在C++中,你可以通过返回对象和指针(智能指针或原始指针)来从函数中返回“东西”,但是在Rust中,返回“东西”的唯一方法就像上面所说的那样,而C++则更接近于返回智能指针?


  • 共1个答案

    匿名用户

    rust和C++都是值类型语言,因此除非明确要求,否则不会在堆上分配对象/结构。 因此,在给出的两种情况下,都没有在堆上分配所讨论的字符串对象/结构。 在这两种语言中,字符串都使用动态分配的备份缓冲区,该缓冲区存储在堆中,但这是一个重要的区别。

    所以在rust中,如果您按值返回,则对象被移动,这始终等同于一个直的memcpy,因为rust结构不允许有自定义的移动逻辑,克隆必须是显式的。 该memcopy将指针复制到备份存储,这样string对象可能在不同的内存中,但备份缓冲区保持不变。

    在C++中,对象可以具有非平凡的复制和(在C++11和更高版本中)移动构造函数。 因此,如果这不是返回命名值,则必须调用复制或移动构造函数。 但是,对于从函数返回的具体情况,复制删除规则就发挥作用了。 这意味着(在C++17和更高版本中,对于一些简单的情况,这是必需的),如果对象是在return语句中初始化的,或者来自具有自动存储持续时间的位置,那么编译器不调用copy/move构造函数,而是将对象直接构造到调用方在返回对象最初创建时提供的存储中,这意味着在返回点不需要复制或移动。 这被称为返回值优化。

    如果在C++11或更高版本中返回的值不是对象初始化值或具有自动存储持续时间的命名值(或者在这些情况下,除了C++17及更高版本中的对象初始化之外,由编译器自行决定),例如调用另一个函数的结果,则将调用move构造函数,在这种情况下,只需将指针复制到后备存储区并清除旧字符串中的指针。 在这种情况下,行为就像生锈一样。 如果该类型有一个更复杂的移动构造函数,那么作为移动的结果,它可以做任何事情。

    最后,在C++98中,如果要返回一个不是对象初始化或具有自动存储持续时间的命名值的值,则将调用复制构造函数,将备份存储复制到新的备份存储,并返回该备份存储。 从而产生一个指向不同内存的新字符串。 当作用域结束时,析构函数将释放旧内存。

    另外,C++实现可以使用小字符串优化,其中小字符串直接存储在string对象中。 在这种情况下,将没有后备存储,并且必须复制字符串,即使对象被移动。

    最后一点要注意的是,在C++11之前,std::string实现通常使用引用计数的后备存储。 在这种情况下,副本将增加备份存储上的引用计数,析构函数将减少其增量,但不释放,因为仍然存在对存储的引用。 在这种情况下,生成的字符串仍将指向原始的后备存储,但代价是比移动过程稍微昂贵一些。 随着move构造函数的引入,这种情况变得不那么常见了。

    为了快速回答第二个问题,rust还允许返回智能指针,指针和引用,但是rust借用检查器将阻止返回对对象的引用本地对象,因为它们没有足够的生存期。 这并不妨碍返回对参数和全局变量(如字符串文本或线程局部变量)的引用,因为它们的生存期比函数长。

    相关问题


    MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(rust|c++|函数|返回|对象)' ORDER BY qid DESC LIMIT 20
    MySQL Error : Got error 'repetition-operator operand invalid' from regexp
    MySQL Errno : 1139
    Message : Got error 'repetition-operator operand invalid' from regexp
    Need Help?