C++使用Smart Pointers来改变指针值

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)

  • @DavidHaim:规则为5/3/0.有了你的建议,我将不得不编写析构函数,移动构造函数/赋值. (8认同)
  • 请注意,在C++ 17中,您可以使用value_t = std :: integral_constant <std :: decay_t <decltype(x)>,x>;`编写`template <auto x>,然后`BarDeleter`只是`value_t <bar_free_recursive>`和`FooDeleter`是`value_t <foo_free>`. (4认同)

Chr*_*rew 5

必须编写.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)