3

这正在成为我的代码中的一种常见模式,因为当我需要管理一个需要不可复制的对象时,因为 A. 它是“重”或 B. 它是操作系统资源,例如关键部分:

class Resource;

class Implementation : public boost::noncopyable
{
    friend class Resource;
    HANDLE someData;
    Implementation(HANDLE input) : someData(input) {};
    void SomeMethodThatActsOnHandle() {
        //Do stuff
    };
public:
    ~Implementation() { FreeHandle(someData) };
};

class Resource
{
    boost::shared_ptr<Implementation> impl;
public:
    Resource(int argA) explicit {
        HANDLE handle = 
            SomeLegacyCApiThatMakesSomething(argA);
        if (handle == INVALID_HANDLE_VALUE)
            throw SomeTypeOfException();
        impl.reset(new Implementation(handle));
    };
    void SomeMethodThatActsOnTheResource() {
        impl->SomeMethodThatActsOnTheHandle();
    };
};

这样,shared_ptr 解决了引用计数问题,允许Resource复制,即使底层句柄只有在所有对它的引用都被销毁后才应该关闭。

Implementation但是,如果我们可以像 boost 的侵入式容器那样将数据移动到内部,似乎我们可以节省分配 shared_ptr 引用计数等的开销。

如果这让过早的优化问题困扰了一些人,我实际上同意我当前的项目不需要这个。但我很好奇这是否可能。

4

4 回答 4

6

使用boost::intrusive_ptr,它旨在处理具有嵌入式引用计数的类。

基于此处示例的未测试示例:

class Resource; 

class Implementation : public boost::noncopyable 
{ 
    friend class Resource;
    HANDLE someData;
    int refCount; // The reference count.
    Implementation(HANDLE input) : someData(input) { refCount = 0; }; 
    void SomeMethodThatActsOnHandle() { 
        //Do stuff 
    }; 
public: 
    ~Implementation() { FreeHandle(someData) }; 
};

intrusive_ptr_add_ref(Implementation* imp) { imp->refCount++; }
intrusive_ptr_release(Implementation* imp) { if(--imp->refCount) == 0) delete imp; }

class Resource 
{ 
    boost::intrusive_ptr<Implementation> impl; 
public: 
    Resource(int argA) explicit { 
        HANDLE handle =  
            SomeLegacyCApiThatMakesSomething(argA); 
        if (handle == INVALID_HANDLE_VALUE) 
            throw SomeTypeOfException(); 
        impl.reset(new Implementation(handle)); 
    }; 
    void SomeMethodThatActsOnTheResource() { 
        impl->SomeMethodThatActsOnTheHandle(); 
    }; 
}; 
于 2010-04-02T20:38:31.890 回答
6

部分解决方案是用于make_shared创建您shared_ptr的 s. 例如,

auto my_thing = std::make_shared<Thing>();

代替

auto my_thing = std::shared_ptr<Thing>(new Thing);

它仍然是非侵入性的,因此无需更改任何其他内容。make_shared结合引用计数和对象本身的内存分配的良好实现。这样可以节省内存分配并保持计数接近对象以获得更好的局部性。它的效率不如boost:intrusive_ptr,但值得考虑。

于 2010-04-02T21:06:22.420 回答
1

您可以通过简单地摆脱这两个类并只拥有一个类并为它定义一个共享 ptr 来节省一些开销 - 这是我一直使用的 idom

 class Resource
 {
      ...
 };
 typedef boost::shared_ptr<Resource> ResourcePtr;
于 2010-04-02T20:39:14.937 回答
1

如果您想减少对象和引用计数器的不同内存分配的开销,您可以尝试使用make_shared。这就是它的用途。

于 2010-04-02T20:44:37.810 回答