手动递增和递减一个boost :: shared_ptr的?(Manually incrementi

2019-07-19 18:47发布

有没有办法手动递增和递减在C ++中一个shared_ptr的计数?

我试图解决的问题如下。 我在写C库++但接口必须是纯C.国内,我想用的shared_ptr来简化存储管理,同时保留通过C接口传递原始指针的能力。

当我通过接口传递一个原始指针,我想递增的引用计数。 随后,客户端将负责调用将递减引用计数,当它不再需要传递的对象的功能。

Answer 1:

在您的建议

然后,客户机将负责递减计数器。

意味着问题的客户端负责内存管理,以及你的信任她。 我还是不明白为什么。

这是不可能的实际修改的shared_ptr计数器...(哼哼,我会在到底该怎么解释......),但也有其他的解决方案。

解决方案1:完整的所有权转让给客户端

交出指针到客户端( 的shared_ptr ::释放 ),并期望它通过所有权还给你当回叫(或简单地删除对象,如果它是不是真的共享)。

这实际上是与裸指针打交道时,传统的做法,并在这里同样适用。 缺点是,你实际上释放所有权仅此shared_ptr的 。 如果对象实际上是共享的 ,可能证明不方便......所以我承担。

解决方法2:用一个回调

该解决方案意味着你始终保持所有权,并负责维护,只要客户需要它这个对象还活着(和脚踢)。 当客户端与对象做,你还指望她告诉你了,调用代码中的回调将执行必要的清理。

struct Object;

class Pool // may be a singleton, may be synchronized for multi-thread usage
{
public:
  int accept(boost::shared_ptr<Object>); // adds ptr to the map, returns NEW id
  void release(int id) { m_objects.erase(id); }

private:
  std::map< int, boost::shared_ptr<Object> > m_objects;
}; // class Pool

这样一来,你的客户“递减”的柜台实际上是你的客户打电话与您使用的ID的回调方法,你删除一个shared_ptr的:)

黑客的boost :: shared_ptr的

正如我说,这是可能的(因为我们是在C ++)实际攻入的shared_ptr。 甚至有几种方法可以做到这一点。

最好的方法(和最简单的)只是将文件复制下来以其它名称,然后(my_shared_ptr?):

  • 改变包括警卫
  • 包括在开始真正的shared_ptr
  • 用自己的名字命名的shared_ptr的任何实例(与更改专用公共访问的属性)
  • 删除了在实际文件,以避免冲突已定义的所有东西

这样,您很容易地获得自己的一个shared_ptr,以便您可以访问计数。 它并没有解决直接具有C代码但是访问计数器的问题,您可能需要“简化”的代码在这里被替换它内置的(如果你不是多线程的,其工作原理,是彻头彻尾的灾难如果你是)。

我特意留出了“reinterpret_cast的”伎俩和指针发生偏移的。 只是有这么多的方法来获得在C / C ++ illegit的东西访问!

我可能会建议您不要使用,虽然在黑客? 这两种方案我上面提出应足够解决你的问题。



Answer 2:

也许你正在使用boost :: shared_ptr的防空火炮DLL的界限,什么将无法正常工作。 在这种情况下的boost :: intrusive_ptr可能会帮助你。 这是一个被滥用的一个常见的情况shared_ptr人尝试解决脏黑客...也许我错了,你的情况,但不应该有很好的理由你尝试做;-)

ADDED 07/2010:这些问题似乎比从shared_ptr的本身来更多的从DLL加载/卸载。 即使升压理由没有告诉太多关于案件时boost::intrusive_ptr应优先于shared_ptr 。 我切换到.NET开发并没有按照TR1的关于这个话题的细节,所以要小心这个答案可能不再有效,现在...



Answer 3:

1. A handle?

If you want maximum security, gives the user a handle, not the pointer. This way, there's no way he will try to free it and half-succeed.

I'll assume below that, for simplicity's sake, you'll give the user the object pointer.

2. acquire and unacquire ?

You should create a manager class, as described by Matthieu M. in his answer, to memorize what was acquired/unacquired by the user.

As the inferface is C, you can't expect him to use delete or whatever. So, a header like:

#ifndef MY_STRUCT_H
#define MY_STRUCT_H

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

typedef struct MyStructDef{} MyStruct ; // dummy declaration, to help
                                        // the compiler not mix types

MyStruct * MyStruct_new() ;
size_t     MyStruct_getSomeValue(MyStruct * p) ;
void       MyStruct_delete(MyStruct * p) ;

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // MY_STRUCT_H

Will enable the user to use your class. I used a declaration of a dummy struct because I want to help the C user by not imposing him the use of the generic void * pointer. But using void * is still a good thing.

The C++ source implementing the feature would be:

#include "MyClass.hpp"
#include "MyStruct.h"

MyManager g_oManager ; // object managing the shared instances
                       // of your class

extern "C"
{

MyStruct * MyStruct_new()
{
   MyClass * pMyClass = g_oManager.createMyClass() ;
   MyStruct * pMyStruct = reinterpret_cast<MyStruct *>(pMyClass) ;
   return pMyStruct ;
}

size_t MyStruct_getSomeValue(MyStruct * p)
{
   MyClass * pMyClass = reinterpret_cast<MyClass *>(p) ;

   if(g_oManager.isMyClassExisting(pMyClass))
   {
      return pMyClass->getSomeValue() ;
   }
   else
   {
      // Oops... the user made a mistake
      // Handle it the way you want...
   }

   return 0 ;
}

void MyStruct_delete(MyStruct * p)
{
   MyClass * pMyClass = reinterpret_cast<MyClass *>(p) ;
   g_oManager.destroyMyClass(pMyClass) ;
}

}

Note that the pointer to MyStruct is plain invalid. You should not use it for whatever reason without reinterpret_cast-ing it into its original MyClass type (see Jaif's answer for more info on that. The C user will use it only with the associated MyStruct_* functions.

Note too that this code verify the class does exist. This could be overkill, but it is a possible use of a manager (see below)

3. About the manager

The manager will hold, as suggested by Matthieu M., a map containing the shared pointer as a value (and the pointer itself, or the handle, as the key). Or a multimap, if it is possible for the user to somehow acquire the same object multiple times.

The good thing about the use of a manager will be that your C++ code will be able to trace which objects were not "unacquired" correctly by the user (adding info in the acquire/unacquire methods like __FILE__ and __LINE__ could help narrow the bug search).

Thus the manager will be able to:

  1. NOT free a non-existing object (how did the C user managed to acquire one, by the way ?)
  2. KNOW at the end of execution which objects were not unaquired
  3. In case of unacquired objets, destroy them anyway (which is good from a RAII viewpoint) This is somewhat evil, but you could offer this
  4. As shown in the code above, it could even help detect a pointer does not point to a valid class


Answer 4:

你应该在这里做的关注点分离:如果客户通过在一个原始指针,客户端将负责内存管理(即清理之后)。 如果您创建的指针,你将负责内存管理。 这也将帮你是在另一个答案中提到的DLL边界问题。



Answer 5:

我碰到一个用例,我确实需要这样的事情,涉及到IOCompletionPorts和并发性的担忧。 在哈克但符合标准的方法是律师 ,方法是:香草萨特描述这里 。

下面的代码片段是的std :: shared_ptr的由VC11实现:

IMPL文件:

namespace {
    struct HackClass {
        std::_Ref_count_base *_extracted;
    };
}

template<>
template<>
void std::_Ptr_base<[YourType]>::_Reset<HackClass>(std::auto_ptr<HackClass> &&h) {
     h->_extracted = _Rep; // Reference counter pointer
}

std::_Ref_count_base *get_ref_counter(const std::shared_ptr<[YourType]> &p) {
     HackClass hck;
     std::auto_ptr<HackClass> aHck(&hck);

     const_cast<std::shared_ptr<[YourType]>&>(p)._Reset(std::move(aHck));

     auto ret = hck._extracted; // The ref counter for the shared pointer
                                // passed in to the function

     aHck.release(); // We don't want the auto_ptr to call delete because
                     // the pointer that it is owning was initialized on the stack

     return ret;
}

void increment_shared_count(std::shared_ptr<[YourType]> &sp) {
     get_ref_counter(sp)->_Incref();
}

void decrement_shared_count(std::shared_ptr<[YourType]> &sp) {
     get_ref_counter(sp)->_Decref();
}

替换[YourType]与对象的类型,你需要修改计数。 要注意,这是相当哈克,并使用平台特定对象的名字是很重要的。 工作中你必须去通过获得这个功能量可能指示多么糟糕的主意,这是社会。 此外,我玩游戏与auto_ptr的,因为我从shared_ptr的劫持功能发生在一个auto_ptr。



Answer 6:

另一种选择是只动态分配的shared_ptr的拷贝,以增加引用计数,并以递减它释放它。 这保证了我的共享对象将不被C API客户端使用而被破坏。

在下面的代码段中,我为了控制一个shared_ptr使用增量()和减量()。 对于此示例的简单起见,我存储在全局变量中的初始的shared_ptr。

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
using namespace std;

typedef boost::shared_ptr<int> MySharedPtr;
MySharedPtr ptr = boost::make_shared<int>(123);

void* increment()
{
    // copy constructor called
    return new MySharedPtr(ptr);
}

void decrement( void* x)
{
    boost::scoped_ptr< MySharedPtr > myPtr( reinterpret_cast< MySharedPtr* >(x) );
}

int main()
{
    cout << ptr.use_count() << endl;
    void* x = increment();
    cout << ptr.use_count() << endl;
    decrement(x);
    cout << ptr.use_count() << endl;

    return 0;
}

输出:

1
2
1



Answer 7:

最快可能并发无锁管理器(如果你知道你在做什么)。

template< class T >
class shared_pool
{
public:

    typedef T value_type;
    typedef shared_ptr< value_type > value_ptr;
    typedef value_ptr* lock_handle;

shared_pool( size_t maxSize ):
    _poolStore( maxSize )
{}

// returns nullptr if there is no place in vector, which cannot be resized without locking due to concurrency
lock_handle try_acquire( const value_ptr& lockPtr ) {
    static value_ptr nullPtr( nullptr );
    for( auto& poolItem: _poolStore ) {
        if( std::atomic_compare_exchange_strong( &poolItem, &nullPtr, lockPtr ) ) {             
            return &poolItem;
        }
    }
    return nullptr;
}


lock_handle acquire( const value_ptr& lockPtr ) {
    lock_handle outID;
    while( ( outID = try_acquire( lockPtr ) ) == nullptr ) {
        mt::sheduler::yield_passive(); // ::SleepEx( 1, false );
    }
    return outID;
}

value_ptr release( const lock_handle& lockID ) {
    value_ptr lockPtr( nullptr );
    std::swap( *lockID, lockPtr);
    return lockPtr;
}

protected:

    vector< value_ptr > _poolStore;

};

的std ::地图是没有那么快,需要额外的搜索,额外的内存,自旋锁。 但它赋予带把手的方式额外的安全。

BTW,黑客与手动释放/获取似乎是一个更好的方法(在速度和内存使用方面)。 C ++的std更好地在他们班加这样的功能,只是为了让C ++剃刀状。



文章来源: Manually incrementing and decrementing a boost::shared_ptr?