【问题标题】:VC++/CLI: How to prevent an unmanaged object from being destroyed when it goes out of scope?VC++/CLI:如何防止非托管对象在超出范围时被销毁?
【发布时间】:2015-07-25 20:33:30
【问题描述】:

我有一个第三方非托管 C++ 库,它有两个类,我们称它们为 ClassAClassBClassA 有一个方法,我们称之为getTheB(),它返回一个ClassB 的实例——它不返回指向该实例的指针,而是返回实例本身。

我现在为ClassA 编写了一个托管包装器,它又具有一个方法getTheB(),它返回一个包装ClassB 的托管包装器。

来自第三方库的原始ClassB 对象必须通过它的指针交给我的托管包装器,例如:

ThirdParty::ClassB db = delegateForClassA -> getTheB();
ManagedClassB^ mb = gcnew ManagedClassB(&db);

但是,当我的 ClassA 包装器的包装器 getTheB() 完成并且范围结束时,ManagedClassB 实例包含对第三方 ClassB 的悬空引用,并调用后者的析构函数,访问ClassB 的方法时导致有趣的结果。

在我的另一个问题中,有人告诉我以某种方式存储原始 ClassB 对象,但我不知道如何。

那么,如何让第三方ClassB 实例保持活动状态?

【问题讨论】:

  • 如果这个类有一个复制构造函数,你可以像 new ClassB(delegate->getTheB()) 一样实例化它,或者如果它有一个赋值运算符,你可以将它分配给新对象。您也可以创建一个包装类,将实例作为参考传递,例如: class Wrapper { public: Wrapper(ClassB& ref) : _ref(ref) {} private: ClassB& _ref;然后你用 new Wrapper(delegate->getTheB()); 实例化它如果可以这样说,那么它就在包装对象的范围内。
  • 第三方ClassB没有可见的公共构造函数,实例仅由库中其他类的方法返回。使用第二种方法,_ref 来自哪里?它是 VC++ 的“内置”还是库中的?
  • 不,是变量名。实际上我不知道它是否真的是在包装器范围内创建的,或者它是一个悬空引用。

标签: .net visual-c++ c++-cli interop


【解决方案1】:

您可以更改 getTheB 以返回堆分配的 ClassB,或者让 ManagedClassB 创建自己的 db 对象副本。

副本更新:

我假设 ManagedClassB 的构造函数看起来像

public ref class ManagedClassB
{
public:
    ManagedClassB(ClassB* p)
        : m_p(p) { }
...
private:
    ClassB* m_p;
};

你应该简单地把它改成

public ref class ManagedClassB
{
public:
    ManagedClassB(const ClassB& b)
        : m_b(b) { }
...
private:
    ClassB m_b;
};

public ref class ManagedClassB
{
public:
    ManagedClassB(ClassB* p)
        : m_p(new ClassB(*p)) { }
...
private:
    ClassB* m_p;
};

【讨论】:

  • getTheB返回的是实际对象而不是指针,而且我没有库的源代码,所以无法更改。
  • 显然,如果 getTheB 按值返回 ClassB 对象,则 ClassB 要么是可复制的,要么是可移动的。如果它是可复制的,则在 ManagedClassB 中进行复制。否则,移动它。
  • 所以我可以简单地执行以下操作:ClassB db = delegateForClassA -> getTheB(); ClassB* dbCopy = new ClassB(&db);?我明天试试这个。
  • 我不是这个意思。我将编辑我的消息以显示一些伪代码
  • 你的第二个例子有效(你打电话给m_p(new ClassB(*p)) { })。对于第一个,编译器抱怨不支持混合类型。
【解决方案2】:

您应该在适合您使用的范围内分配数据库。你知道什么是最适合你的,不管是静态成员,还是基本的全局成员,或者其他什么。只要您需要 db,范围就必须保持有效,并且在您完成后执行可能会超出范围。

【讨论】:

  • 我不能,因为ClassB 没有公开可见的构造函数。获取实例的唯一方法是getTheB() 方法。我不能让托管方法返回非托管类的直接实例,当我返回 &db 时,只要返回 &db 的方法完成,我就会有一个悬空引用。
  • 我期望某种构造函数,如果只是一个默认构造函数,你目前已经分配了它。它将在进入范围时被分配和(默认)构造,然后在执行到达 ->getTheB() 调用时分配(覆盖)。抱歉,这么绝对的方式,很难放过构造函数。
  • 有一个黑客,如果你没有其他方法。您可以使用自己的库头副本,将构造函数的私有更改为公共,编译器将授予您访问权限。它有效,但它很脏,而且不是我告诉你的。 ;-)
  • Aaaaand 显然这个答案被匿名否决了,因为我提到了这一点。
  • 不,这并没有被否决,但 KABoissoneault 的回答对我有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多