应该何时返回值,而不是返回唯一指针

use*_*314 4 c++ memory-management unique-ptr c++11 return-by-value

我想知道的是,按值传递Cat实际上与返回std::unique_ptr<Cat>传递它们,内存管理以及在实践中使用它们有何不同.

记忆管理明智,不一样吗?因为返回值对象和包装在unique_ptr中的对象,一旦它们超出范围,它们的析构函数会被触发吗?

那么,你将如何比较两段代码:

Cat catFactory(string catName) {
    return Cat(catName);
}

std::unique_ptr<Cat> catFactory(string catName) {
    return std::unique_ptr(new Cat(catName));
}
Run Code Online (Sandbox Code Playgroud)

Bri*_*ian 13

按值返回应视为默认值.(*)违反默认惯例,通过返回std::unique_ptr<Cat>,应该要求理由.

返回指针有三个主要原因:

  1. 多态性.这是返回的最佳理由,std::unique_ptr<Cat>而不是Cat:您实际上可能正在创建派生自的类型的对象Cat.如果你需要这种多态性,你绝对需要返回某种指针.这就是工厂函数通常返回指针的原因.

  2. Cat不能廉价搬家或根本不能搬家."固有的"不可移动的类型是罕见的; 你应该Cat通过使其便宜地移动来尝试修复.但是当然Cat可以是其他人拥有的类型,你不能添加移动构造函数(或者甚至是复制构造函数).在这种情况下,除了使用之外没有太多可以做的事情unique_ptr(并向所有者投诉).

  3. 该函数可能会失败并且无法构造任何有效的函数Cat.在这种情况下,一种可能性是按值返回,但如果Cat不能构造则抛出异常; 另一方面,在C++ 11/C++ 14中,是使函数返回std::unique_ptr<Cat>并在无法Cat构造时返回空指针.但是,在C++ 17中,您应该开始返回std::optional<Cat>而不是std::unique_ptr<Cat>在这种情况下,以避免不必要的堆分配.

(*)这也适用于在被调用的函数需要其自己的值副本时传递对象,例如,将从其一个参数初始化类成员的构造函数.按值接受对象并移动.

  • @ user1113314:这听起来与这篇文章所说的相反.通常,在设计某种API时,您应该使用一般机制:按值返回,除非按值返回是一个非常糟糕的主意. (2认同)

ein*_*ica 5

默认情况下,按值返回。

此规则的例外情况:

  1. Cat 需要存在于堆上,以便比触发其创建的代码更持久……但在这种情况下,也许它不应该真正unique_ptr返回 a,而是shared_ptr.
  2. 您实际上并不是在构建 Cat,而是访问可以被解释为 Cat 的东西;在这种情况下,您可能不需要一个唯一的指针,而是一个常规的指针(或带有自定义删除器的唯一指针)。
  3. 多态性 - 如果它是一个工厂,而 Cat 是它的产品之一,那么您可能还可以制作一只狗和一匹马,它们都是动物,因此您将返回一个指向动物的指针。这绝对是您需要使用唯一指针的情况。
  4. 复制、赋值和/或移动构造函数中存在黑暗巫毒,这使得始终确保只从远处戳你的猫很重要。

我不同意 @Brian 关于他建议的两个例外的回答:

  • 我建议不要使用指针返回类型,以便能够通过返回来指示失败nullptr。未能返回有效值就是异常的原因,即使您想避免它们 - 我建议返回std::expected(使用 C++23)或std::optional(早期的 C++ 版本)。或者如果允许的话,只是在失败时抛出异常。
  • 通常不需要移动构造函数来启动返回值优化 - 因此缺少移动构造函数不应成为返回指针的原因。