有没有办法手动递增和递减在C ++中一个shared_ptr的计数?
我试图解决的问题如下。 我在写C库++但接口必须是纯C.国内,我想用的shared_ptr来简化存储管理,同时保留通过C接口传递原始指针的能力。
当我通过接口传递一个原始指针,我想递增的引用计数。 随后,客户端将负责调用将递减引用计数,当它不再需要传递的对象的功能。
有没有办法手动递增和递减在C ++中一个shared_ptr的计数?
我试图解决的问题如下。 我在写C库++但接口必须是纯C.国内,我想用的shared_ptr来简化存储管理,同时保留通过C接口传递原始指针的能力。
当我通过接口传递一个原始指针,我想递增的引用计数。 随后,客户端将负责调用将递减引用计数,当它不再需要传递的对象的功能。
在您的建议
然后,客户机将负责递减计数器。
意味着问题的客户端负责内存管理,以及你的信任她。 我还是不明白为什么。
这是不可能的实际修改的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,以便您可以访问计数。 它并没有解决直接具有C代码但是访问计数器的问题,您可能需要“简化”的代码在这里被替换它内置的(如果你不是多线程的,其工作原理,是彻头彻尾的灾难如果你是)。
我特意留出了“reinterpret_cast的”伎俩和指针发生偏移的。 只是有这么多的方法来获得在C / C ++ illegit的东西访问!
我可能会建议您不要使用,虽然在黑客? 这两种方案我上面提出应足够解决你的问题。
也许你正在使用boost :: shared_ptr的防空火炮DLL的界限,什么将无法正常工作。 在这种情况下的boost :: intrusive_ptr可能会帮助你。 这是一个被滥用的一个常见的情况shared_ptr
人尝试解决脏黑客...也许我错了,你的情况,但不应该有很好的理由你尝试做;-)
ADDED 07/2010:这些问题似乎比从shared_ptr的本身来更多的从DLL加载/卸载。 即使升压理由没有告诉太多关于案件时boost::intrusive_ptr
应优先于shared_ptr
。 我切换到.NET开发并没有按照TR1的关于这个话题的细节,所以要小心这个答案可能不再有效,现在...
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.
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)
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:
你应该在这里做的关注点分离:如果客户通过在一个原始指针,客户端将负责内存管理(即清理之后)。 如果您创建的指针,你将负责内存管理。 这也将帮你是在另一个答案中提到的DLL边界问题。
我碰到一个用例,我确实需要这样的事情,涉及到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。
另一种选择是只动态分配的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
最快可能并发无锁管理器(如果你知道你在做什么)。
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 ++剃刀状。