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

101*_*010 2 c c++ pointers void-pointers

我正在尝试将C++类(例如class foo)与C 接口.到目前为止,我所做的是定义一个C结构,它包含一个不透明的指针成员变量(即void*),指向相关的C++ foo对象.

struct C_foo {
  void *foo_obj;
};
Run Code Online (Sandbox Code Playgroud)

我定义了一个alloc()C接口函数来分配类型的对象C_foo:

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

  return out;
};
Run Code Online (Sandbox Code Playgroud)

我现在要做的是创建一个dealloc()C接口函数,该函数将正确释放C_foo先前分配的类型的对象,如上alloc()所示:

void dealloc(struct C_foo *obj) {
  /* ??? */
}
Run Code Online (Sandbox Code Playgroud)

我知道显式删除void*指针(即delete obj->foo_obj;)会导致未定义的行为(§5.3.5/ 1 [expr.delete]):

delete-expression的结果的类型为void[81].

[81]这意味着无法使用类型指针删除对象, 因为它不是对象类型.void* void

题:

我将如何正确解除struct C_foo对象的释放?

Mik*_*our 12

如果你知道(肯定)它指向什么类型,那么施放:

delete static_cast<foo*>(obj->foo_obj);
Run Code Online (Sandbox Code Playgroud)

如果你忘记了类型,那么你需要重新设计.

  • 因为如果您知道正确的类型并且只想将指针转换为它,那么这是正确的强制转换.`reinterpret_cast`用于强制编译器进行通常不允许的转换(这里不适用),`void*`的`dynamic_cast`甚至不编译 (5认同)

And*_*rew 5

如果由于某些C API而需要传递不透明的句柄,并且对象具有完全不同的类型,则可以使用下面概述的方法。

请注意,如果所有类型都共享一个公共base,则可以只为base提供一个虚拟析构函数,static_castvoid*指向base的指针delete。这是一种比我在下面概述的方法更为普遍的方法。

句柄结构

这将需要持有一个指向分配的对象yuo的指针,以及一个指向对该类型进行编码的对象的指针(以便您可以将其删除):

struct Handle {
    void* handle;
    void* deleter_info;
};
Run Code Online (Sandbox Code Playgroud)

C ++实现细节

您将有一些课程。这些就是您想要传递给...实例的句柄的东西。

class Foo;
class Bar;
// etc
Run Code Online (Sandbox Code Playgroud)

您还需要删除器的基类:

struct deleter_base {
    virtual void destroy(Handle h) = 0;
    virtual ~deleter_base() {}
};
Run Code Online (Sandbox Code Playgroud)

...和一个类模板,以产生知道相关类型的派生类:

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

创建对象

对于要提供其句柄的每种类型,您都需要一个函数来创建一个句柄:

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;
}
Run Code Online (Sandbox Code Playgroud)

摧毁物体

您将需要一个destroy函数:

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
}
Run Code Online (Sandbox Code Playgroud)

笔记

该结构可以包含a deleter_base* deleter_info而不是void* deleter_info。这实际上是一个口味问题,以及是否要struct deleter_info;在C API中使用。将其存储在一个void*隐藏的实现细节中,使其真正变得不透明。

为了能够有意义地使用该句柄,您还需要对其他信息进行编码,以便能够从该void* handle成员中检索有用的信息。通常,变体类型使用枚举成员来执行此操作。另外,您可以希望用户足够聪明,只将其句柄传递回需要正确类型的句柄的函数。您可以使用不同的句柄结构类型(struct HandleFoo;,,struct HandleBar;...)来强制执行此操作,并且仍在void*内部使用成员来保持不透明度。