题
有没有一种方法手动增加和减少数的情况在C++?
问题是我正试图解决的是如下。我写一个图书馆在C++但接口,必须在纯C。在内部,我想到使用的情况,以简化存管理,同时保留的能力,通过一个原指通过的C界面。
当我传递了一个原始指针,通过该接口,我想到增加的参数。客户随后将负责调功能,这将减量的参考数,当其不再需要传递的对象。
解决方案
在你的建议
客户随后将负责递减计数器。
意味着客户在的问题是负责存储管理,并且你信任她。我还是不明白为什么。
这是不可能的实际修改的情况反...(嗡嗡声,我将解释底怎么...),但还有其他的解决方案。
解决方案1:完全所有权的客户
把指针指向客户(情况::释放),并期望它通过的所有权回到你的时候叫回(或简单地删除的对象,如果它是不是真的共享)。
这实际上是传统方法在处理指针和原则在这里也适用。不利的一面是,实际上,你释放所有权 对于这种情况只.如果对象是实际上 共享 这可能证明不方便的...所以原谅我.
解决方案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
这种方法,你的客户'减量'的计实际上是你的客户叫回调方法与标识使用,和你删除的一个情况:)
黑客提升::情况
正如我所说的这是可能的(由于我们是用C++)实际上入侵的情况.甚至有几种方法来这样做。
的 最好的 方法(和最简单的)只是简单地将文件复制下的另一个名称(my_shared_ptr?) 然后:
- 改变包括警卫
- 包括真实情况开始
- 重命名的任何实例的情况有自己的名字(和改变私人公共访问的特性)
- 删除所有的东西已经是定义中的真实文件,以避免冲突
这样你很容易获得的情况的你自己的,你可以访问该数。它并不解决问题的具有对C码直接访问的反虽然,你可能需要简化'这里的代码来代替它由一个内置(其工作的如果你不是多线程,并为彻头彻尾的灾难性的,如果你是)。
我故意留出的'reinterpret_cast'招和指针的偏移的。只是有这么多的方法来获得illegit访问东西在C/C++!
我能建议你不要使用黑客吗?这两个解决方案,我提出了上应该足以解决你的问题。
其他提示
也许你正在使用boost :: shared_ptr的防空火炮DLL的界限,什么将无法正常工作。在这种情况下的boost :: intrusive_ptr 可以帮助你出。这是shared_ptr
被滥用的一个常见的情况的人尝试解决脏黑客...也许我错了,你的情况,但不应该有很好的理由做你试着做什么; - )
ADDED 07/2010:这些问题似乎比从shared_ptr的本身来更从DLL加载/卸载。即使升压理由没有告诉太多关于案件时boost::intrusive_ptr
应优于shared_ptr
。我切换到.NET开发并没有按照TR1的细节关于这个话题,所以要小心这个答案可能不再有效,现在...
1.把手?
如果你想要最大安全,使用户手柄,不指针。这种方式,有没有办法,他将尝试 free
它和半成功。
我会假设下,为简单起见,你就会得到用户的对象的指针。
2.获取和unacquire?
你应该创建一个管理类,以描述马修M.在他 答案, ,要记住的是获取/unacquired过。
作为该接口是C,你不能指望他使用 delete
或什么的。因此,一个标题,如:
#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
将使用户能够使用。我用了一个宣言》的一个虚拟的结构,因为我想帮C户通过不实行他的使用通用的 void *
指针。但使用 void *
仍然是一件好事。
C++源实现的功能将是:
#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) ;
}
}
注意,针对MyStruct是普通无效。你不应该将它用于任何原因没有reinterpret_cast-ing入其原MyClass类型(见Jaif的 答案 更多信息。C用户将使用它只与相关MyStruct_*功能。
注意到,这个代码检验类确实存在。这可能是矫枉过正,但它是一个可能的使用的管理(见下文)
3.有关经理
管理者将举行,作为建议通过马修,地图包含的共同指针作为一个价值(以及指本身,或处理,因为键)。或基于,如果这是可能的用户以某种方式获得相同的对象多次。
的好事对使用一个经理将是你C++编码将能够跟踪其对象不是"unacquired"正确地用户(增加信息获取/unacquire方法一样 __FILE__
和 __LINE__
可以帮助缩小的错误搜索)。
这样的经理将能够:
- 不是免费的,非现有的对象(怎C户的管理,以获得一个方法吗?)
- 知道结束时执行对象是不unaquired
- 在情况下的unacquired艺术品,摧毁他们无论如何(这是很好的从RAII观点) 这是有点邪恶,但是你可以提供这个
- 如上述代码,它甚至可以帮助检测指不指向一个有效的类
您应该做的关注点分离的位置:如果客户端通过在原始指针时,客户端将负责存储器管理(即清理之后)。如果您创建的指针,你将负责内存管理。这也将帮你是在另一个答案中提到的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 ::地图不是那么快,需要额外的搜索,额外的内存,旋锁。 但它赋予带手柄的方法更高的安全性。
顺便说一句,与劈手动释放/获取似乎是一个更好的方法(在速度和内存使用的术语)。 C ++的std更好地在他们类添加这样的功能,只是为了保持C ++剃刀形的。