【问题标题】:C++/CLI Wrapping a Function that Returns a std::shared_ptrC++/CLI 包装返回 std::shared_ptr 的函数
【发布时间】:2011-09-04 13:37:29
【问题描述】:

我目前正在使用 C++/CLI 包装 C++ 类,以实现 .NET 互操作性,遵循在托管类中保存本机指针的标准流程。在一个例子中,我有一个本地类,它的功能如下:

std::shared_ptr<BaseChannel> channelData(const int RunNumber);

我已经开始为BaseChannel 创建一个包装类。但是,如果我将原始指针传递给托管类的构造函数,则无法保证托管类指向的对象的生命周期。 IE。 shared_ptr 可能超出范围,对象将被删除,托管类将保留一个悬空指针。

这种情况的常见解决方案是什么?

更新

@Ben:所以我像这样包装了包含上述问题中的方法的类(假设它在一个名为 Node 的本机类中,它被包装在一个名为 NodeRef 的托管类中:

ChannelUser^ NodeRef::ChannelData(int runNumber)
{
    // mpNode is native class pointer of type Node held in managed class
    // wrapper called NodeRef
    std::shared_ptr<BaseChannel> spBaseChannel = mpNode->channelData(runNumber);

    // ChannelUser is using clr_scoped_ptr to hold the shared_ptr
    ChannelUser^ channelUser = gcnew ChannelUser(spBaseChannel);
    return channelUser;
}

因为 shared_ptr 在通过引用传递给托管类时没有增加其引用计数,这是否意味着

只要这个 shared_ptr 在 范围,它指向的对象将 仍然存在,因为它的引用 计数至少为 1

? (参考C++ - passing references to std::shared_ptr or boost::shared_ptr)

【问题讨论】:

  • 引用计数增加了(一次是由spBaseChannel 的构造函数,一次是由ChannelUser ctor-initializer 中的new shared_ptr)...但这东西不是当std::shared_ptr 不是时,它不会是线程安全的。
  • @Ben:我更新了最后一个问题以正确表达我的担忧。但是,无论如何,我认为您已经在之前的评论中回答了这个问题。引用计数器在 ChannelUser ctor-initializer 中增加。

标签: .net c++-cli wrapper shared-ptr mixed-mode


【解决方案1】:

这是一个托管的shared_ptr&lt;T&gt;。您可以直接从 shared_ptr 分配给它,它会获取一个副本,当托管对象被 GC 或处置时它将删除。

例子:

m_shared_ptr<CupCake> cupCake0(new CupCake());
m_shared_ptr<CupCake> cupCake1 = new CupCake();
m_shared_ptr<CupCake> cupCake2 = shared_ptr<CupCake>(new CupCake());
m_shared_ptr<CupCake> cupCake3 = make_shared<CupCake>();
shared_ptr<CupCake> cupCake4 = (shared_ptr<CupCake>)cupCake3;

代码:

#pragma once

#include <memory>

template <class T>
public ref class m_shared_ptr sealed
{
    std::shared_ptr<T>* pPtr;

public:
    m_shared_ptr() 
        : pPtr(new std::shared_ptr<T>()) 
    {}

    m_shared_ptr(T* t) {
        pPtr = new std::shared_ptr<T>(t);
    }

    m_shared_ptr(std::shared_ptr<T> t) {
        pPtr = new std::shared_ptr<T>(t);
    }

    m_shared_ptr(const m_shared_ptr<T>% t) {
        pPtr = new std::shared_ptr<T>(*t.pPtr);
    }

    !m_shared_ptr() {
        delete pPtr;
    }

    ~m_shared_ptr() {
        delete pPtr;
    }

    operator std::shared_ptr<T>() {
        return *pPtr;
    }

    m_shared_ptr<T>% operator=(T* ptr) {
        delete pPtr;
        pPtr = new std::shared_ptr<T>(ptr);
        return *this;
    }

    T* operator->() {
        return (*pPtr).get();
    }

    void reset() {
        pPtr->reset();
    }
};

【讨论】:

  • 很棒的答案。被低估了。
  • m_shared_ptr的赋值运算符不是先删除p,是不是内存泄漏?
  • 我认为您可以随意编辑和修复此答案
  • 我想出了一些我需要的额外方法: // Useful for accessing shared_ptr members like 'use_count' const std::shared_ptr&lt;T&gt; &amp; get() { return *pPtr; } // Useful when passing to another managed object std::shared_ptr&lt;T&gt;* share() { return new std::shared_ptr&lt;T&gt;(*pPtr); } // Allows assignment via std::make_shared m_shared_ptr&lt;T&gt;% operator=(const std::shared_ptr&lt;T&gt;&amp; t) { delete pPtr; pPtr = new std::shared_ptr&lt;T&gt;(t); return *this; } ~
  • Brilliant 正要自己写这样的东西。感谢您为我节省了一些时间! :D
【解决方案2】:

shared_ptr 是原生类型,托管对象不能有完整的原生子对象。

但是,正如您所注意到的,托管对象可以具有指向本机对象的指针。您需要的是一个指向shared_ptr 的指针,它将被视为对BaseChannel 对象的引用,并防止它被过早释放。

当然,使用智能指针而不是原始shared_ptr&lt;BaseChannel&gt;* 有很多理由。我写了一个应该合适的智能指针,你可以在 codereview.stackexchange.com 上找到它:"scoped_ptr for C++/CLI (ensure managed object properly frees owned native object)"


示例(未编译测试):

ref class ChannelUser
{
    clr_scoped_ptr<shared_ptr<BaseChannel>> chan_ptr;

public:
    ChannelUser( shared_ptr<BaseChannel>& chan ) : chan_ptr(new shared_ptr<BaseChannel>(chan)) {}
};

这会自动实现IDisposable,并在Dispose 或终结器运行时删除shared_ptr,从而减少BaseChannel 上的引用计数。

【讨论】:

  • +1 - 您能否举例说明如何在您的回答中使用您的clr_scoped_ptr
  • @Seth:已经完成,希望对您有所帮助。如果您发现任何重大错误,请告诉我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多