我建议你阅读这个页面。您可以在 D 中调用的 C++ 类上的唯一函数是虚函数。这意味着
D 不能调用 C++ 特殊成员函数,反之亦然。这些包括构造函数、析构函数、转换运算符、运算符重载和分配器。
当你在 D 中声明一个 C++ 类时,你使用一个extern(C++) interface
. 所以,你的类/结构看起来像这样
extern(C++) interface A
{
void fun();
}
但是,您需要另一个extern(C++)
函数来分配任何类型的对象A
,因为它是 C++ 代码必须这样做,因为 D 代码无权访问任何构造函数。您还需要一种方法将其传递回 C++ 代码,以便在完成后将其删除。
现在,如果您想将该接口包装在一个类型中,该类型将调用extern(C++)
函数来构造它并调用extern(C++)
函数来删除它(这样您就不必担心手动执行此操作),那么您是否使用类或 struct 完全取决于你想用它做什么。
一个类将是一个引用类型,它反映了 C++ 类的实际情况。因此,无需您做任何特别的事情,就可以传递它。但是,如果您想要保证包装的 C++ 对象已被释放,则必须手动执行此操作,因为无法保证 D 类的终结器将永远运行(并且大概是您放置代码的地方)调用 C++ 函数来删除 C++ 对象)。您必须使用clear
(实际上将destroy
在编译器的下一个版本中重命名为 - dmd 2.060)来销毁 D 对象(即调用其终结器并处理其任何值类型的成员变量的销毁) ,否则您必须调用 D 对象上的一个函数,该函数调用 C++ 函数来删除 C++ 对象。例如
extern(C++) interface A
{
void fun();
}
extern(C++) A createA();
extern(C++) void deleteA(A a);
class Wrapper
{
public:
this()
{
_a = createA();
}
~this()
{
deleteA(_a);
}
auto opDispatch(string name, Args...)(Args args)
{
return mixin("_a." ~ name ~ "(args)");
}
private:
A _a;
}
void main()
{
auto wrapped = new Wrapper();
//do stuff...
//current
clear(wrapped);
//starting with dmd 2.060
//destroy(wrapped);
}
但这确实有缺点,如果您不调用clear
/ destroy
,并且垃圾收集器永远不会收集您的包装器对象,deleteA
则永远不会在 C++ 对象上调用。这可能很重要,也可能无关紧要。这取决于 C++ 对象是否真的需要在程序终止之前调用它的析构函数,或者如果 GC 永远不需要收集包装器,当程序终止时它是否可以让它的内存返回给操作系统(而不调用它的析构函数)目的。
如果你想要确定性破坏,那么你需要一个结构。这意味着您需要担心将结构变成引用类型。否则,如果它被复制,当其中一个被销毁时,C++ 对象将被删除,而另一个结构将指向垃圾(然后它将在被销毁时尝试删除)。要解决这个问题,您可以使用std.typecons.RefCounted
. 然后你会得到类似的东西
extern(C++) interface A
{
void fun();
}
extern(C++) A createA();
extern(C++) void deleteA(A a);
struct Wrapper
{
public:
static Wrapper opCall()
{
Wrapper retval;
retval._a = createA();
return retval;
}
~this()
{
if(_a !is null)
{
deleteA(_a);
_a = null;
}
}
auto opDispatch(string name, Args...)(Args args)
{
return mixin("_a." ~ name ~ "(args)");
}
private:
A _a;
}
void main()
{
auto wrapped = RefCounted!Wrapper();
//do stuff...
}
您还可以定义包装器,使其具有引用计数逻辑并避免RefCounted
,但这肯定会更复杂。
无论如何,我绝对不建议您使用 abool
来标记包装器是否拥有 C++ 对象的建议,因为如果原始包装器对象在所有副本之前被销毁,那么您的副本将指向垃圾。
如果您确实希望使用 C++ 对象的复制构造函数(因此将 C++ 对象视为值类型),则另一个选择是添加一个extern(C++)
函数,该函数采用 C++ 对象并返回它的副本,然后在 postblit 中使用它.
extern(C++) A copyA(A a);
this(this)
{
if(_a !is null)
_a = copyA(a);
}
希望这能让事情变得足够清楚。