通过传递指针的地址来初始化std :: unique_ptr

Ton*_*ion 24 c++ unique-ptr c++11

我正在创建一个与一些Windows API代码交互的类,现在我必须初始化的一个指针是通过调用初始化它的本机函数来完成的.

我的指针是std::unique_ptr带有自定义删除器的类型,它调用所提供的WinAPI删除函数,但是我不能将带有&address-of运算符的unique_ptr传递给init-function.为什么?

我创建了一个演示我的问题的示例:

#include <memory>

struct foo
{
   int x;
};

struct custom_deleter {};

void init_foo(foo** init)
{
  *init = new foo();
}

int main()
{
   std::unique_ptr<foo, custom_deleter> foo_ptr;

   init_foo(&foo_ptr);
}
Run Code Online (Sandbox Code Playgroud)

编译器咆哮并说:

source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
Run Code Online (Sandbox Code Playgroud)

Ste*_*sop 20

在某个地方,unique_ptr<foo>有一个类型的数据成员foo*.

但是,类的用户直接修改该数据成员是不合法的.这样做不一定会保留类的不变量unique_ptr,特别是它不会释放旧的指针值(如果有的话).在您的特殊情况下,您不需要这样做,因为之前的值为0,但一般情况下应该发生.

因此,unique_ptr不提供对数据成员的访问权限,仅提供对其值的副本(via get()operator->).你不能得到foo**你的unique_ptr.

你可以写:

foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);
Run Code Online (Sandbox Code Playgroud)

出于异常安全的原因,这std::unique_ptr<foo, custom_deleter> foo_ptr(new foo());是异常安全的:unique_ptr保证您传递给其构造函数的任何内容最终都会使用删除器删除.

顺便说一句,custom_deleter不需要operator()(foo*)吗?还是我错过了什么?

  • @Tony:`shared_ptr`与`unique_ptr`相同.如果你需要使用像`init_foo`这样处理原始指针的接口,那么是的,你需要使用原始指针.然后,您将原始指针尽快放入合适的智能指针中. (3认同)

sbi*_*sbi 6

史蒂夫已经解释了技术问题是什么,然而,潜在的问题更加深入:当你处理裸指针时,代码使用了一个有用的习语.为什么这段代码首先进行两步初始化(首先创建对象,然后初始化它)?既然你想使用智能指针,我建议你仔细调整代码:

foo* init_foo()
{
  return new foo();
}

int main()
{
   std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );

}
Run Code Online (Sandbox Code Playgroud)

当然,重命名init_foo(),以create_foo()和有它返回一个std::unique_ptr<foo>直接效果会更好.此外,当您使用两步初始化时,通常建议考虑使用类来包装数据.

  • @Tony:sbi的代码只是"更干净","main"中的单行完成了它所说的内容.我的代码需要一个临时变量才能创建你真正想要的东西.这不是更可取的,我的代码就是你要做的就是调用`init_foo`. (2认同)