【问题标题】:How to wrap class that passes unmanaged class in constructor?如何包装在构造函数中传递非托管类的类?
【发布时间】:2015-10-17 17:44:38
【问题描述】:

我正在尝试了解 C++/CLI,以便为 C++ 代码创建 Wrapper 类。我的问题是我有一个类存储指向该类的父对象的指针,因此我需要将它传递给该类。

下面是一个例子,但是完整的类有更多的功能并存储了额外的数据。

class A
{
private:
    A* parent;

public:
    A(A* Parent)
    {
        parent = Parent;
    }
    ~A()
    {
        delete parent;
    }

    A* GetParent()
    {
        return parent;
    }
}

我目前的想法是拥有一个非公共构造函数,以便您可以使用非托管类构造一个托管类,而无需在类外部访问它。

public ref class ManagedA
{
    A* unmanaged;

    ManagedA(A* unmanaged)
    {
        this->unmanaged = unmanaged;
    }

public:
    ManagedA(ManagedA^ Parent)
    {
        unmanaged = new A(Parent->unmanaged);
    }

    ~ManagedA()
    {
        delete unmanaged;
        unmanaged = NULL;
    }

    ManagedA^ GetParent()
    {
        return gcnew ManagedA(unmanaged->GetParent());
    }
}

虽然这适用于类内的函数,但如果我想创建一个对象或者我有一个需要传入非托管类的函数,我仍然会遇到问题。

有没有办法解决这个问题?

【问题讨论】:

    标签: visual-c++ c++-cli wrapper unmanaged managed


    【解决方案1】:
    ManagedA^ GetParent()
    {
        return gcnew ManagedA(unmanaged->GetParent());
    }
    

    你正在用这样的代码打断你的腿,这是非常危险的。问题是,您正在创建多个引用完全相同的 A* 的 ManagedA 对象。一旦其中一个被销毁,所有其他 ManagedA 对象现在都有一个悬空指针。这几乎肯定会导致内存损坏。

    解决方法很简单,在构造函数中存放父引用即可:

    public ref class ManagedA {
    private:
        A* unmanaged;
        ManagedA^ parent;
    public:
        ManagedA(ManagedA^ Parent) : parent(Parent) {
            A* unmanagedParent = Parent == nullptr ? nullptr : Parent->unmanaged;
            unmanaged = new A(unmanagedParent);
        }
        ManagedA^ GetParent() {
            return parent;
        }
        ~ManagedA() {
            this->!ManagedA();
            unmanaged = NULL;
        }
        !ManagedA() {
            delete unmanaged;
        }
    };
    

    请注意在构造函数中添加的 nullptr 检查,这是我可以看到第一个 A* 是如何创建的唯一理智的方法。

    这是一个简单的情况,当您需要将 A* 映射回 ManagedA^ 时,就会出现完全相同的对象身份问题。首先检查这是否真的需要,客户端代码预计只会操作 ManagedA 对象。必要时,您需要创建一个查找表,以便可靠地找到相应的 ManagedA^。这需要一个静态的Dictionary<IntPtr, ManagedA^>。在构造函数中将对象添加到字典中,在终结器中删除它们。请注意,您忘记包含终结器,它不是可选的。我将它添加到 sn-p 中。

    【讨论】:

    • 是的,添加 nullptr 是第一个对象的初始化方式,例如链表的头部或树的根。尽管我仍在努力解决问题的第二部分。如果我有另一个托管包装类,并且需要将 A* 对象转换为 ManagedA^ 对象怎么办?我真的把自己写到了这个角落......
    • 我描述了你需要做什么。我还是不买,A*是哪里来的?你永远不会从使用你的包装器的客户端代码中得到它,它完全不知道 A* 的存在。
    • 我能回答的最好方法是 A* 就像一个层次结构树,其中每个节点都有一个父节点和许多子节点,因此在创建它时,第一个节点的父节点为 nullptr,然后下一个创建的将作为孩子添加到第一个。
    • 问题是我还有一个类,它有一个返回 A* 对象的函数,所以包装器应该返回 ManagedA^。那么如何在该包装类中将该 A* 转换为 ManagedA^?
    • 不要在问题中忽略这一点。但是,很明显,你需要那个字典。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-13
    • 1970-01-01
    • 2021-10-19
    • 2015-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多