简体   繁体   English

如何正确删除作为void指针的对象?

[英]How to properly delete an object that is a void pointer?

I'm trying to interface a C++ class (eg, class foo ) to C. What I have done so far is to define a C structure that holds an opaque pointer member variable (ie, void* ), that points to the associated C++ foo object. 我正在尝试将C ++类(例如, class foo )连接到C.到目前为止我所做的是定义一个C结构,它包含一个不透明的指针成员变量(即void* ),指向相关的C ++ foo对象。

struct C_foo {
  void *foo_obj;
};

I defined an alloc() C interface function that allocates objects of type C_foo : 我定义了一个alloc() C接口函数,它分配C_foo类型的C_foo

struct C_foo* alloc(/* input */) {
  struct C_foo *out = new struct C_foo;
  out->foo_obj      = new foo(/* input */);

  return out;
};

What I want to do now, is to create a dealloc() C interface function that will properly deallocate objects of type C_foo previously allocated with alloc() shown above: 我现在要做的是创建一个dealloc() C接口函数,该函数将正确释放先前使用上面显示的alloc()分配的C_foo类型的对象:

void dealloc(struct C_foo *obj) {
  /* ??? */
}

I know that explicitly deleting the void* pointer (ie, delete obj->foo_obj; ) would results in undefined behaviour ( § 5.3.5/1 [expr.delete] ): 我知道显式删除void*指针(即delete obj->foo_obj; )会导致未定义的行为( §5.3.5/ 1 [expr.delete] ):

The delete -expression's result has type void [81]. delete -expression的结果类型为void [81]。

[81] This implies that an object cannot be deleted using a pointer of type void* because void is not an object type . [81]这意味着无法使用 void* 类型的指针删除对象, 因为void不是对象类型

Question: 题:

How I'm going to properly deallocate a struct C_foo object? 我将如何正确释放struct C_foo对象?

If you know (for sure) what type it points to, then cast: 如果你知道(肯定)它指向什么类型,那么施放:

delete static_cast<foo*>(obj->foo_obj);

If you've lost track of the type, then you'll need to redesign. 如果你忘记了类型,那么你需要重新设计。

If you need to pass around opaque handles because of some C API, and your objects have totally disparate types, you can use an approach like the one outlined below. 如果由于某些C API而需要传递不透明句柄,并且您的对象具有完全不同的类型,则可以使用如下所述的方法。

Note that if your types all share a common base , you can just provide a virtual destructor for the base, static_cast the void* to a pointer to base, then delete that. 请注意,如果您的类型共享一个公共基础 ,您可以只为基础提供虚拟析构函数, static_castvoid*指向base,然后delete它。 This is a much more common approach than the one I outline below. 这是一种比我下面概述的方法更常见的方法。

The handle struct 句柄结构

This will need to hold a pointer to the object yuo allocated, and a pointer to something that encodes the type (so that you can delete it): 这将需要保存指向对象yuo的指针,以及指向编码该类型的东西的指针(以便您可以删除它):

struct Handle {
    void* handle;
    void* deleter_info;
};

The C++ Implementation Detail C ++实现细节

You'll have some classes; 你会有一些课程; these are the things you want to pass around handles to instances of... 这些是你想要将句柄传递给...的实例的东西

class Foo;
class Bar;
// etc

You'll also need a base class for your deleter: 你还需要一个删除基类:

struct deleter_base {
    virtual void destroy(Handle h) = 0;
    virtual ~deleter_base() {}
};

... and a class template to produce derived classes that know the relevant type: ...和一个类模板,用于生成知道相关类型的派生类:

template<typename T> struct deleter {
    virtual void destroy(Handle h)
    {
        T* ptr = static_cast<T*>(h.handle);
        delete ptr;
    }
};

Creating objects 创建对象

For each type you want to provide handles to, you'll need a function to create a handle: 对于要为其提供句柄的每种类型,您需要一个函数来创建句柄:

Handle create_foo_handle()
{
    Handle h = {0};
    h.ptr = new foo;
    h.deleter_info = new deleter<foo>;
    return h;
}

Handle create_bar_handle()
{
    Handle h = {0};
    h.ptr = new bar;
    h.deleter_info = new deleter<bar>;
    return h;
}

Destroying objects 销毁物体

You'll need a destroy function: 你需要一个销毁功能:

void destroy(Handle h)
{
    deleter_base* deleter = static_cast<deleter_base*>(h.deleter_info);
    deleter->destroy(h); // delete the foo, or bar, or whatever
    delete deleter; // delete the deleter
}

Notes 笔记

The struct could hold a deleter_base* deleter_info as opposed to void* deleter_info . 结构可以包含deleter_base* deleter_info而不是void* deleter_info This is really a matter of taste, and whether you want a struct deleter_info; 这真的是一个品味问题,你是否想要一个struct deleter_info; in your C API. 在您的C API中。 Storing it in a void* hides the implementation details, making it truly opaque. 将其存储在void*隐藏了实现细节,使其真正不透明。

To be able to use the handle meaningfully, you'll also need to encode some other information to be able to retrieve something useful from the void* handle member. 为了能够有意义地使用句柄,您还需要编码一些其他信息,以便能够从void* handle成员中检索有用的东西。 Typically, variant types do this with an enum member. 通常,变体类型使用枚举成员执行此操作。 Alternatively, you could hope your users are smart enough to only pass their handles back to a function that expects a handle of the correct type. 或者,您可能希望您的用户足够聪明,只能将其句柄传递回需要正确类型句柄的函数。 You could have different handle struct types ( struct HandleFoo; , struct HandleBar; , ...) to enforce this, and still use void* members internally to maintain opacity. 您可以使用不同的句柄结构类型( struct HandleFoo;struct HandleBar; ,...)来强制执行此操作,并且仍然在内部使用void*成员来维护不透明度。

转换为正确的类型:

delete static_cast<foo*>(obj->foo_obj);

Create a common base class with a virtual destructor, and use that instead of void*. 使用虚拟析构函数创建公共基类,并使用它而不是void *。

You could do something like the following. 您可以执行以下操作。

class GenericBase
{
public:
    virtual ~GenericBase() = 0;
};

inline GenericBase::~GenericBase() {} // or put in source file without inline

template<class T>
class GenericWrapper : public GenericBase
{
public:
    typedef T Type;
    Type x;

    GenericWrapper() {}
    GenericWrapper(const Type& x) : x(x) {}
};

You can use dynamic_cast to convert GenericBase* to a concrete type, in order to benefit from safety-checking. 您可以使用dynamic_cast将GenericBase *转换为具体类型,以便从安全检查中受益。

I just noticed you want to use this with C. Obviously you can't pass a GenericBase* to C. But you can pass it to C as a void* and cast back to GenericBase* when you need to delete it. 我只是注意到你想在C中使用它。显然你不能将GenericBase*传递给C.但是你可以将它作为void*传递给C并在你需要删除时转回GenericBase*

I will assume that you always put an instance of the same type into that void* . 我将假设您总是将相同类型的实例放入该void*

In which case, pImpl time: 在这种情况下, pImpl时间:

struct foo_impl; // note, just a name
struct C_foo {
  foo_impl *foo_obj; // can use pointers to undefined structs in both C and C++
};

now most of your problem goes away. 现在你的大部分问题都消失了。 C treats foo_obj as an opaque pointer. C将foo_obj视为不透明指针。

In C++ we include another header file (sample fields): 在C ++中,我们包含另一个头文件(示例字段):

// in C++ **only** header file -- C does not see this:
struct foo_impl {
  int x;
  std::vector<double> v;
  foo_impl( int, double const* b, double const* e ); // constructor
};

// functions exposed to C, but implemented in C++ with visibility of the above foo_impl
extern "C" struct C_foo* alloc(int x, double const* b, double const* e) {
 struct C_foo *out = new struct C_foo;
 out->foo_obj      = new foo_impl(x, b, e);

 return out;
};

extern "C" void dealloc(struct C_foo *obj) {
  delete obj->foo_obj;
  delete obj;
}

and you win. 你赢了

Note that a struct is just a name for a class in C++ with default public instead of default private . 请注意, struct只是C ++中使用默认public而不是default privateclass的名称。

I changed the name from foo to foo_impl , and created some sample data in it. 我将名称从foo更改为foo_impl ,并在其中创建了一些示例数据。

If you could put more than one different kind of type into your void* , I would first advise putting a pure-virtual interface class with a virtual destructor, and basically following the above steps. 如果您可以在void*多种不同类型的类型,我首先建议将纯虚拟接口类与虚拟析构函数放在一起,基本上遵循上述步骤。


Now, there are cases where you actually want to store more than one distinct, unrelated type in your opaque pointer. 现在,在某些情况下,您确实希望在不透明指针中存储多个不同的,不相关的类型。 These are not that common. 这些并不常见。 But in those cases, we will want to store a destroy function. 但在这些情况下,我们会想要存储一个destroy函数。

Again, prefer my above approach, but if it doesn't work, we have this one. 再次,更喜欢我的上述方法,但如果它不起作用,我们就有这个。

There are a few ways to store the deleter function: 有几种方法可以存储删除函数:

typedef void(*foo_deleter)(void*);
struct C_foo {
  void* foo_obj;
  foo_deleter* deleter;
};

another approach is: 另一种方法是:

struct foo_impl;
struct C_foo {
  foo_impl* foo_obj;
};

// elsewhere:
typedef void(*foo_deleter)(foo_impl*);
struct foo_impl {
  foo_deleter* deleter;
};

template<typename T>
struct foo_details {
  foo_impl header;
  T* data;
  ~foo_details() { delete data; }
  foo_details( T* in ):data(in) {}
  foo_details( foo_details const& ) = delete;
  foo_details& operator=( foo_details const& ) = delete;
  foo_details():data(nullptr) { header.deleter=nullptr; }
};

then allocate a foo_details to stick into the foo_obj storing a foo , reinterpret_cast to a foo_impl (valid under standard layout clauses), and store into foo_obj . 然后分配一个foo_details来坚持存储foofoo_objreinterpret_castfoo_impl (在标准布局子句下有效),并存储到foo_obj

The deleter would then take a foo_impl , reinterpret_cast to a foo_details<foo> and delete . 然后deleterfoo_implreinterpret_cast转换为foo_details<foo>delete

To access the data, you'd have to figure out what type it is (you can stick extra type information in the foo_impl , like an integer or whatever), then reinterpret_cast to the appropriate foo_details<?> and access the data in it. 要访问数据,你必须弄清楚它是什么类型(你可以在foo_impl额外的类型信息,如整数或其他),然后reinterpret_cast到适当的foo_details<?>并访问其中的data

Realize that you'll need to be able to extract the type information of your opaque pointer somehow in order to use it: consider using whatever mechanism you use there to also determine how to delete it. 意识到你需要能够以某种方式提取不透明指针的类型信息才能使用它:考虑使用你在那里使用的任何机制来确定如何删除它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM