0
votes

I am trying to understand C++/CLI so I can create Wrapper classes for C++ code. My problem is that I have a class which stores a pointer to a parent object of that class, therefore I need to pass it into the class.

Below is an example, but the full class has more functions and stores extra data.

class A
{
private:
    A* parent;

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

    A* GetParent()
    {
        return parent;
    }
}

My current idea is to have a non-public constructor so that you can construct a managed class with the unmanaged one without it being accessible outside the class.

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());
    }
}

While this works for functions inside the class, I still have issues if I want to create an object or if I have a function that needs the unmanaged class to be passed in.

Is there a way I can work around this?

1

1 Answers

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

You are shooting your leg off with code like this, it is very dangerous. Problem is, you are creating multiple ManagedA objects that refer to the exact same A*. As soon as one of them gets destroyed, all the other ManagedA objects now have a dangling pointer. That's almost guaranteed to cause memory corruption.

The solution is very simple, just store the parent reference in the constructor:

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;
    }
};

Note the added nullptr check in the constructor, the only sane way I could see how the very first A* could ever get created.

That was the easy case, the exact same object identity problem occurs when you ever need to map an A* back to a ManagedA^. First check if this is really needed, the client code is expected to only ever manipulate ManagedA objects. When necessary, you'll need to create an lookup table so you can reliably find the corresponding ManagedA^ back. That requires a static Dictionary<IntPtr, ManagedA^>. Add objects to the dictionary in the constructor, remove them in the finalizer. Note that you forgot to include a finalizer, it is not optional. I added it to the snippet.