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

Posted

技术标签:

【中文标题】使用已使用 new X() 创建的变量将 std::unique_ptr 传递给函数【英文标题】:Passing std::unique_ptr to a function by using a variable that is already created using new X() 【发布时间】:2021-06-21 15:43:51 【问题描述】:

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

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

注意:Y 是 X 的超类


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

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

注意: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++ 不是很精通。谢谢

【问题讨论】:

是的。我不明白为什么我仍然可以在第一种情况下访问处理程序,即使我像 std::unique_ptr() 一样传递它。 handler_ 是如何声明的? 【参考方案1】:

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

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

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

这避免了在无论如何都要移动构造时显式构造传递给方法的新unique_ptr。说真的,你可能根本不想想要this-&gt;handler_,如果你正在做的只是立即离开它,那就去做吧:

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

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

【讨论】:

其实我想要的是这里的shared_ptr。这两个类都应该能够使用 handler_。 (一个类要求处理程序发送消息,而另一个类在收到消息时进行回调)。但是方法 grpcStreamHandler() 需要一个 unique_ptr。所以我别无选择。有什么推荐的方法吗? @LahiruUdayanga:unique_ptr 有终身保修吗?它是否可以作为grpcStreamHandler 返回的对象的属性访问? unique_ptr 被销毁后,我可以向该类发送回调,让该类知道 handler_ 已被销毁。 @LahiruUdayanga:好的,如果您注册该回调并将handler_ 清空,这并非完全不合理(假设同时访问该对象不是问题)。我肯定会评论创建它的代码和析构函数(无法清理它),以确保没有急切的海狸添加手动 delete。这是丑陋/代码气味,但如果你被框架束缚,那就这样吧。 无并发访问。这都是单线程的。是的,这就是要走的路。非常感谢您的帮助【参考方案2】:

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

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

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

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

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

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

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

【讨论】:

我不会说“销毁”,该函数可以将指针转移到其他地方。当然,原始指针不包含有关指向对象生命周期的信息。 是的,所以我在这里尝试启动一个 gRPC 流并通过它发送一些消息。即使在调用 grpcStreamHandler() 之后,我调用的类也应该能够访问 handler_。但是该方法将 std::unique_ptr 作为参数。所以对我有用的唯一方法是方法 1。但是正如你所说,如果 unique_ptr 被破坏,我不会知道它,可能会调用 handler->send() 。有什么想法吗?

以上是关于使用已使用 new X() 创建的变量将 std::unique_ptr 传递给函数的主要内容,如果未能解决你的问题,请参考以下文章

C#使用 new 关键字创建对象 IMap newMap = new MapClass();

是否有必要使用 std::atomic 来表示线程已完成执行?

将 opengl Window 转换为 std::size_t 并再次在 Linux 中有效,但在 OS X 中无效

std::make_unique 和 std::unique_ptr 内部有 new 的区别

在 COM 对象上使用 std::map?

似乎我不能将 MS 检漏仪用于新表达式“new (std::nothrow)”。那是对的吗?