Visual c++ 以非托管对象作为托管代码的参数高效调用非托管方法

Visual c++ 以非托管对象作为托管代码的参数高效调用非托管方法,visual-c++,c++-cli,Visual C++,C++ Cli,我有下面的场景。托管代码将初始化类的大量对象,该类是非托管结构的包装器。有两种方法我可以做到这一点。一种是有一个托管类包装器,它只有一个指向非托管对象的指针。另一种方法是拥有一个完整的托管类,并在需要调用非托管方法时创建非托管对象。我提供了以下两种方法。有人告诉我,如果我使用方法1(有一个指向非托管对象的指针),GC在了解非托管部分时会有很多问题,最好使用方法2。有没有人告诉我哪个更好,或者是否有其他更好的方法。我对方法2的担忧是,每次调用非托管方法时都会有来回的复制。我不确定GC问题是否超过了

我有下面的场景。托管代码将初始化类的大量对象,该类是非托管结构的包装器。有两种方法我可以做到这一点。一种是有一个托管类包装器,它只有一个指向非托管对象的指针。另一种方法是拥有一个完整的托管类,并在需要调用非托管方法时创建非托管对象。我提供了以下两种方法。有人告诉我,如果我使用方法1(有一个指向非托管对象的指针),GC在了解非托管部分时会有很多问题,最好使用方法2。有没有人告诉我哪个更好,或者是否有其他更好的方法。我对方法2的担忧是,每次调用非托管方法时都会有来回的复制。我不确定GC问题是否超过了它

编辑-第一种方法有一个ref类,第二种方法有一个value类。第二个是值的原因是它可以更有效地添加到列表中

在非托管状态下:

struct A_UNMANAGED
{
    int a;
    int b[20];
};

void GetData(A_UNMANAGED& a); // populates A

在管理模式下(第一种方法)

public ref class A\u管理
{
A_无管理*ap;
公众:
物业系统::UInt32 a
{
System::UInt32 get(){return ap->a;}
无效集(系统::UInt32值){ap->a=value;}
}
属性数组^b
{
数组^get(){return ap->b;}
void set(数组^value){b=value;}//假定此副本有效
}
内部:
void GetData()
{
获取数据(ap);
}
};

在托管(第二种方法)中(编辑:更新为ref。假设所有垃圾收集和指针创建都正确写入)

A类公共价值管理
{
系统:UInt32 a;
数组^b;
公众:
物业系统::UInt32 a
{
System::UInt32 get(){返回一个;}
无效集(系统::UInt32值){a=value;}
}
属性数组^b
{
数组^get(){return b;}
无效集(数组^value){b=value;}
}
内部:
void GetUnmanaged(A_UNMANAGED&obj1)
{
obj1.a=a;
pin_ptr bp=&b[0];
memcpy(obj1.b,bp,20);
}
void GetData()
{
A_非托管obj2;
GetUnmanaged(obj2);
GetData(obj2);
//从obj2复制到成员变量
}
};

不,第一个代码片段是以规范的方式进行的。垃圾收集器只移动指针,不移动指向的对象。如果该运算符应该已分配malloc()或新运算符,则无法移动该运算符

在代码中还有几个严重的问题。除非GetData()通过引用获取其参数,否则您似乎不会为非托管的_分配内存。从未调用GetData()。这通常必须是一个ref类(而不是ref值),因此可以提供析构函数和终结器来释放内存。
b
属性设置程序将使用StackOverflowException轰炸您的程序。在处理这个项目之前,一定要学习语言


检查示例代码。

正如Hans所说,第一种方法是通常的方法(尽管我个人认为,在这种特殊情况下,p/Invoke会更简洁…)。但是,您的
A_MANAGED::b
实现将无法工作,如果您尝试简单地编译它,这是显而易见的。请尝试以下方法:

public ref class A_MANAGED
{
    A_UNMANAGED* ap;

public:
    A_MANAGED() : ap(new A_UNMANAGED() ) { }
    ~A_MANAGED() { this->!A_MANAGED(); }
    !A_MANAGED() { delete ap; ap = nullptr; }

    property int a
    {
        int get() { return ap->a; }
        void set(int value) { ap->a = value; }
    }

    property array<int>^ b
    {
        array<int>^ get()
        {
            using System::Runtime::InteropServices::Marshal;
            array<int>^ arr = gcnew array<int>(20);
            Marshal::Copy(System::IntPtr(ap->b), arr, 0, 20);
            return arr;
        }
        void set(array<int>^ value)
        {
            using System::Runtime::InteropServices::Marshal;
            Marshal::Copy(value, 0, System::IntPtr(ap->b), 20);
        }
    }

internal:
    void GetData()
    {
        ::GetData(*ap);
    }
};
public ref class A\u管理
{
A_非托管*应付账款;
公众:
A_托管():ap(新A_非托管()){}
~A_MANAGED(){这个->!A_MANAGED();}
!A_MANAGED(){delete ap;ap=nullptr;}
属性int a
{
int get(){return ap->a;}
无效集(int值){ap->a=value;}
}
属性数组^b
{
数组^get()
{
使用System::Runtime::InteropServices::封送;
数组^arr=gc新数组(20);
封送:复制(系统::IntPtr(ap->b),arr,0,20);
返回arr;
}
无效集(数组^value)
{
使用System::Runtime::InteropServices::封送;
封送:复制(值,0,系统::IntPtr(ap->b),20);
}
}
内部:
void GetData()
{
::GetData(*ap);
}
};

还有一个关于从属性返回数组的常见警告:这是一个坏主意。除非您真的想与非托管类的公共接口保持奇偶性,
b
实际上应该是一对set/get函数,而不是一个属性。

我想标记第一个类引用。我有析构函数和终结器,但没有在这里编写它们。同样,正如我所指定的,假设b副本有效。我的问题是要知道哪种方法更好,如果GC和值问题是真的,那么你通过发布蹩脚的代码浪费了我的时间。这样做的目的是什么?否则它不会改变我的答案。
public value class A_MANAGED
{
    System::UInt32 a;
    array<System::UInt32>^ b;

public:
    property System::UInt32 a
    {
        System::UInt32 get() { return a; }
        void set(System::UInt32 value) { a = value; }
    }

    property array<System::UInt32>^ b
    {
        array<System::UInt32>^ get() { return b; }
        void set(array<System::UInt32>^ value) { b = value; }
    }

internal:
    void GetUnmanaged(A_UNMANAGED& obj1)
    {
        obj1.a = a; 
        pin_ptr<System::UInt32> bp = &b[0];
        memcpy(obj1.b, bp, 20);
    }

    void GetData()
    {
        A_UNMANAGED obj2;
        GetUnmanaged(obj2);
        GetData(obj2);
        // copy from obj2 to member variables
    }
};
public ref class A_MANAGED
{
    A_UNMANAGED* ap;

public:
    A_MANAGED() : ap(new A_UNMANAGED() ) { }
    ~A_MANAGED() { this->!A_MANAGED(); }
    !A_MANAGED() { delete ap; ap = nullptr; }

    property int a
    {
        int get() { return ap->a; }
        void set(int value) { ap->a = value; }
    }

    property array<int>^ b
    {
        array<int>^ get()
        {
            using System::Runtime::InteropServices::Marshal;
            array<int>^ arr = gcnew array<int>(20);
            Marshal::Copy(System::IntPtr(ap->b), arr, 0, 20);
            return arr;
        }
        void set(array<int>^ value)
        {
            using System::Runtime::InteropServices::Marshal;
            Marshal::Copy(value, 0, System::IntPtr(ap->b), 20);
        }
    }

internal:
    void GetData()
    {
        ::GetData(*ap);
    }
};