使用已使用 new X() 创建的变量将 std::unique_ptr 传递给函数

问题描述

我是 C++ 新手,对以下 2 个场景中发生的事情有些困惑。

  1. 我使用 new 关键字创建变量,然后将该变量作为 std::unique_ptr(x) 传递给函数。当我这样做时,我仍然可以从我传递的类中访问 this->handler_。

注意:Y是X的超类


this->handler_ = new X(this);
auto res1 = root()->grpcStreamHandler(std::unique_ptr<Y>( this->handler_));

  1. 将变量创建为唯一指针并直接传递。

注意:Y是X的超类

this->handler_ = std::unique_ptr<Y>(new X(this));
auto res1 = root()->grpcStreamHandler(std::unique_ptr<Y>( std::move(handler_)));


对于第二种情况,我知道 std::move() 移动对象并将所有权转移给我们传递的函数。但是在第一种情况下实际发生了什么?如果有人能解释清楚,我将不胜感激,因为我对 C++ 不是很精通。谢谢

解决方法

在第一种情况下,会创建一个 unique_ptr 实例,并且分配的内存开始按其生命周期进行管理,即发生隐式所有权转移。

,

在第一种情况下,this->handler_ 是一个指针,unique_ptr 承担 this->handler_ 指向的对象的所有权。
旧指针在该对象以常规方式销毁之前一直有效。
除非对象的所有者delete拥有它,否则不允许将指针传递给 release

在第二种情况下,this->handler_ 表示具有唯一但可转让所有权的对象。
在您将所有权转移给新所有者后,原所有者将一无所有。

,

在第一种情况下,this->handler_ 最终包含一个它不拥有的指针(unique_ptr 拥有它,并且当 delete 被销毁时将 unique_ptr 它除非它是手动从 unique_ptr 中提取的)。如果有任何尝试 delete this->handler_(例如,析构函数通常在具有指针成员的合理类上执行的操作),您正在双重释放内存(未定义的行为),或者如果他们尝试在 unique_ptr 之后使用它清理它,您正在访问释放的内存(也未定义)。

基本上,选项 #1 几乎肯定是错误的,即使它没有错,它也是最糟糕的代码味道(您分配并存储为实例属性的指针,而不是 delete?颤抖)。您需要选项 #2,或者更准确地说,它的更简单版本:

auto res1 = root()->grpcStreamHandler(std::move(handler_));

这避免了显式构造传递给方法的新 unique_ptr,而它无论如何都是移动构造的。实际上,如果您所做的只是立即摆脱它,您可能根本不想要 this->handler_,只需:

auto res1 = root()->grpcStreamHandler(std::unique_ptr<Y>(new X(this)));

并将其构造/传递为单个动作(这可能可以省略移动)。

,

在第一种情况下,您使用原始指针构造 std::unique_ptr

在这种情况下,您正在通过构造它来将指针的所有权转移到 unique_ptr。 RAII 代表 Resource Aquisition Is Iinitialization。您使用要拥有的指针初始化唯一指针,该唯一指针负责清理它。

调用该函数后,this->handler_ 是一个指向可能被破坏的对象的非空指针。

我建议总是使用第二种情况,甚至更好(如果你有 C++14):

this->handler_ = std::make_unique<X>(this);
auto res1 = root()->grpcStreamHandler(std::move(this->handler_));

make unique 函数将负责更新对象。