Search code examples
pointersc++-cliunmanagedmanaged

C++/CLI: wrapping the same unmanaged object in multiple managed objects


I am developing a library which has two layers, unmanaged (C++) and managed (C++/CLI). The unmanaged layer contains the logics and the computation algorithms, while the managed layer provides interface and visualisation to a .NET-based host application. A class in the managed layer wraps its class counterpart in the unmanaged layer, e.g. ManagedA wraps UnmanagedA and ManagedB wraps UnmanagedB.

Classes in the unmanaged layer have query methods, suppose UnmanagedA::B() returns an instance of UnmanagedB. For visualisation, I need to wrap this instance in a ManagedB instance. The problem is, if I repeat this process twice, I am creating two ManagedB instances which points to the same UnmanagedB instance. Because the ManagedB instances are disposed, the same UnmanagedB instance is deleted twice, which should not happen.

So I would like to know the best practice or strategy to wrap an unmanaged object in a managed object.

Here is a code which emulates this behaviour. I understand that you don't need to explicitly delete the managed objects, but I use it here just to emulate the deletion sequence.

Many thanks.

#include "stdafx.h"

using namespace System;

class UnmanagedB
{
public:
    UnmanagedB() {}
    ~UnmanagedB() {}

    int i = 0;
};

class UnmanagedA
{
public:
    UnmanagedA(UnmanagedB* pUnmanagedB)
    : m_pUnmanagedB(pUnmanagedB)
    {
    }

    ~UnmanagedA() {}

    UnmanagedB* B() { return m_pUnmanagedB; }

protected:
    UnmanagedB* m_pUnmanagedB;
};

public ref class ManagedA : IDisposable
{
public:
    ManagedA(UnmanagedA* pUnmanagedA)
        : m_pUnmanagedA(pUnmanagedA)
    {

    }

    ~ManagedA()
    {
        delete m_pUnmanagedA;
    }

private:
    UnmanagedA* m_pUnmanagedA;
};

public ref class ManagedB : IDisposable
{
public:
    ManagedB(UnmanagedB* pUnmanagedB)
        : m_pUnmanagedB(pUnmanagedB)
    {

    }

    ~ManagedB()
    {
        delete m_pUnmanagedB;
    }

private:
    UnmanagedB * m_pUnmanagedB;
};

int main(array<System::String ^> ^args)
{
    UnmanagedB* pUnmanagedB = new UnmanagedB();
    UnmanagedA* pUnmanagedA = new UnmanagedA(pUnmanagedB);

    ManagedB^ pManagedB1 = gcnew ManagedB(pUnmanagedA->B());
    ManagedB^ pManagedB2 = gcnew ManagedB(pUnmanagedA->B()); 
    delete pManagedB1;
    delete pManagedB2; // will crash here because the destructor deletes pUnmanagedB, which is already deleted in the previous line
    delete pUnmanagedA;
    return 0;
}

Solution

  • This is a typical case using a smart pointer.

    So don't store UnmanagedA* and UnmanagedB* use shared_ptr and shared_ptr

    Becaus ethe managed class can only carry a plain pointer to an unmannged class you have to redirect it again and use:

    shared_ptr<UnmanagedA>* pManagedA;
    

    A simple accessor function will help you to use the pointer:

    shared_ptr<UnmanagedA> GetPtrA() { return *pManagedA; }
    

    All plain pointer to the unmanaged classes should be shared_ptr instances. In your main use make_shared instead of new. Or direct the pointer created by new into a shared_ptr...

    Here is one class rewritten:

    public ref class ManagedA : IDisposable
    {
    public:
        ManagedA(shared_ptr<UnmanagedA> pUnmanagedA)
        {
            m_pUnmanagedA = new shared_ptr<UnmanagedA>();
            *m_pUnmanagedA = pUnmanagedA;
        }
    
        ~ManagedA()
        {
            delete m_pUnmanagedA;
        }
    
        void Doit()
        {
            GetPtrA()->DoSomething();
        }
    private:
        shared_ptr<UnmanagedA>* m_pUnmanagedA;
        shared_ptr<UnmanagedA> GetPtrA() { return *m_pUnmanagedA; }
    };