muf*_*fel 23 c++ smart-pointers
考虑一个C库,它定义了用于创建,销毁和使用自定义结构的函数
struct Foo;
void foo_action(Foo*);
Foo* foo_create();
void foo_free(Foo*);
Run Code Online (Sandbox Code Playgroud)
目前,我在我的C++项目中使用了库,如下所示
Foo* myfoo = foo_create();
foo_action(myfoo);
foo_free(myfoo);
Run Code Online (Sandbox Code Playgroud)
我理解为什么智能指针很重要并希望迁移我的代码以使用它们.这就是代码现在的样子.
#include <memory>
#include <functional>
typedef std::unique_ptr<Foo, std::function<void(Foo*)>> FooPtr;
// ...
FooPtr myfoo2(foo_create(), foo_free);
foo_action(myfoo2.get());
Run Code Online (Sandbox Code Playgroud)
它似乎工作,但myfoo2.get()调用看起来很hacky.我按预期使用它吗?
该库的另一部分创建并使用某种列表结构.api看起来像
struct Bar;
Bar* bar_append(Bar*, int);
void bar_free_recursive(Bar*);
Run Code Online (Sandbox Code Playgroud)
并用作
// using NULL as current Bar* creates the initial structure
Bar* bar = bar_append(NULL, 1);
// each invocation leads to another 'head' structure
bar = bar_append(bar, 42);
bar = bar_append(bar, 123);
Run Code Online (Sandbox Code Playgroud)
当指针(指向的地址)随每次bar_append调用而改变时,我如何在这里引入智能指针,以便bar_free_recursive在释放指针实例时在当前指针值上调用?
Jar*_*d42 21
但是myfoo2.get()调用看起来很hacky.我按预期使用它吗?
它不是hacky,你按预期使用它.
我会更进一步将整个包装在一个类中:
struct Foo;
void foo_action(Foo*);
Foo* foo_create();
void foo_free(Foo*);
class FooWrapper
{
public:
FooWrapper() : mFoo(foo_create()) {}
void action() { foo_action(mFoo.get()); }
private:
struct FooDeleter
{
void operator()(Foo* foo) const { foo_free(foo); }
};
std::unique_ptr<Foo, FooDeleter> mFoo;
};
Run Code Online (Sandbox Code Playgroud)
以同样的方式:
struct Bar;
Bar* bar_append(Bar*, int);
void bar_free_recursive(Bar*);
class BarWrapper
{
public:
explicit BarWrapper(int n) : mBar(bar_append(nullptr, n)) {}
void append(int n) { mBar.reset(bar_append(mBar.release(), n)); }
private:
struct BarDeleter
{
void operator()(Bar* bar) const { bar_free_recursive(bar); }
};
std::unique_ptr<Bar, BarDeleter> mBar;
};
Run Code Online (Sandbox Code Playgroud)
必须编写.get()是使用智能指针的一个不幸的结果,但如果你想传递给一个接受非拥有,可以为空的指针的函数,我认为最好的做法.
但是,在实践中,我经常发现你不需要它可以为空,并且可以接受引用而不是原始指针.然后语法不那么"hacky":
void foo_action(Foo&); // accept a reference instead of a raw-pointer
struct FooDeleter {
void operator()(Foo* foo) const { foo_free(foo); }
};
using FooPtr = std::unique_ptr<Foo, FooDeleter>;
FooPtr make_foo() {
return FooPtr(foo_create());
}
int main() {
auto foo = make_foo();
// ...
if (foo) { // check for null
foo_action(*foo); // dereference smart-pointer
}
}
Run Code Online (Sandbox Code Playgroud)
bar_append应该与unique_ptr您提供的服务一起使用std::move:
struct BarDeleter {
void operator()(Bar* bar) const { bar_free_recursive(bar); }
};
using BarPtr = std::unique_ptr<Bar, BarDeleter>;
BarPtr bar_append(BarPtr bar, int value) {
return BarPtr(bar_append(bar.release(), value));
}
int main() {
BarPtr bar;
bar = bar_append(std::move(bar), 42);
bar = bar_append(std::move(bar), 123);
}
Run Code Online (Sandbox Code Playgroud)