c++ - punteros - smart pointer c programming
C++ utiliza punteros inteligentes con valores de puntero cambiantes (3)
pero la invocación myfoo2.get () parece hacky. ¿Lo estoy usando según lo previsto?
No es hacky, lo usas como es debido.
Yo iría un paso más allá y envolvería todo en una clase:
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;
};
Del mismo modo:
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;
};
Considere una biblioteca de C que defina funciones para crear, destruir y trabajar con una estructura personalizada
struct Foo;
void foo_action(Foo*);
Foo* foo_create();
void foo_free(Foo*);
Actualmente, utilicé la biblioteca en mi proyecto C ++ de la siguiente manera
Foo* myfoo = foo_create();
foo_action(myfoo);
foo_free(myfoo);
Entiendo por qué los punteros inteligentes son importantes y quiero migrar mi código para usarlos. Así es como se ve el código ahora.
#include <memory>
#include <functional>
typedef std::unique_ptr<Foo, std::function<void(Foo*)>> FooPtr;
// ...
FooPtr myfoo2(foo_create(), foo_free);
foo_action(myfoo2.get());
Parece que funciona, pero la invocación de myfoo2.get()
parece hacky. ¿Lo estoy usando según lo previsto?
Hay otra parte de la biblioteca que crea y trabaja con algún tipo de estructura de lista. El api se parece a
struct Bar;
Bar* bar_append(Bar*, int);
void bar_free_recursive(Bar*);
y se usa como
// 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);
A medida que el puntero (la dirección apuntada) cambia con cada invocación de bar_append
, ¿cómo introduciría punteros inteligentes aquí, para que se invoque bar_free_recursive
en el valor del puntero actual cuando se libera la instancia del puntero?
Tener que escribir .get()
es una consecuencia desafortunada del uso de punteros inteligentes, pero creo que es una buena práctica si desea pasar a una función que acepte un puntero que no admite .get()
se puede usar.
Pero, en la práctica, a menudo descubro que no es necesario que sea anulable y puede aceptar una referencia en lugar de un puntero en bruto. Entonces la sintaxis es un poco menos "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
}
}
bar_append
debería funcionar con unique_ptr
siempre y cuando uses 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);
}
Yo diría que myfoo2.get()
es torpe , no hacky .
Personalmente crearía un envoltorio de plantilla basado en obj_ptr
(usted elige un nombre más relevante) y usaría rasgos para cada tipo de objeto para modelar su requerimiento a la manera de C ++. La envoltura puede entonces eliminar el desorden de acceder al objeto subyacente.
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;
}