Подтвердить что ты не робот

С++ использует интеллектуальные указатели с изменяющимися значениями указателя

Рассмотрим библиотеку C, которая определяет функции для создания, уничтожения и работы с настраиваемой структурой

struct Foo;
void foo_action(Foo*);
Foo* foo_create();
void foo_free(Foo*);

В настоящее время я использовал библиотеку в своем проекте на С++ следующим образом

Foo* myfoo = foo_create();
foo_action(myfoo);
foo_free(myfoo);

Я понимаю, почему интеллектуальные указатели важны и хотят перенести мой код, чтобы использовать их. Вот как выглядит код.

#include <memory>
#include <functional>
typedef std::unique_ptr<Foo, std::function<void(Foo*)>> FooPtr;
// ...
FooPtr myfoo2(foo_create(), foo_free);
foo_action(myfoo2.get());

Кажется, что работает, но вызов myfoo2.get() кажется взломанным. Использую ли я его как предназначение?

Там другая часть библиотеки, которая создает и работает с какой-то структурой списка. Апи выглядит как

struct Bar;
Bar* bar_append(Bar*, int);
void bar_free_recursive(Bar*);

и используется как

// 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);

Как указатель (адрес, на который указывает) изменяется с каждым вызовом bar_append, как мне ввести интеллектуальные указатели здесь, так что bar_free_recursive вызывается в текущем значении указателя при освобождении экземпляра указателя?

4b9b3361

Ответ 1

но вызов myfoo2.get() кажется взломанным. Я использую его по назначению?

Это не хаки, вы используете его, как предполагалось.

Я бы сделал еще один шаг и обернул целое в класс:

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;
};

Точно так же:

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;
};

Ответ 2

Наличие записи .get() является неудачным следствием использования интеллектуальных указателей, но я думаю, что лучше всего использовать, если вы хотите перейти к функции, которая принимает указатель, не имеющий прав, с нулевым значением.

Но на практике я часто нахожу, что вам не нужно, чтобы он был нулевым и мог принимать ссылку вместо raw-pointer. Тогда синтаксис немного менее "взломан":

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
    } 
}

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);
}

Ответ 3

Я бы сказал, что myfoo2.get() неуклюж, не хаки.

Я лично создал обертку на основе шаблона obj_ptr (вы выберете более релевантное имя) и примените черты для каждого типа объекта, чтобы смоделировать ваше требование на С++. Затем оболочка может удалить неудобство доступа к базовому объекту.

template <typename T, typename Traits>
class obj_ptr final
{
    std::unique_ptr<Foo, void(*)(T*)> ptr_{ Traits::create(), Traits::free };

public:
    operator T*() { return ptr_.get(); }

    operator const T*() const { return ptr_.get(); }

    T* operator->() { return ptr_.get(); }

    const T* operator->() const { return ptr_.get(); }
};

class foo_traits
{
public:
    static Foo* create() { return foo_create(); }

    static void free(Foo* foo) { foo_free(foo); }
};

int main()
{
    using FooPtr2 = obj_ptr<Foo, foo_traits>;

    FooPtr2 myfoo2;

    foo_action(myfoo2);

    return EXIT_SUCCESS;
}