有没有一种方法手动增加和减少数的情况在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__ 可以帮助缩小的错误搜索)。

这样的经理将能够:

  1. 不是免费的,非现有的对象(怎C户的管理,以获得一个方法吗?)
  2. 知道结束时执行对象是不unaquired
  3. 在情况下的unacquired艺术品,摧毁他们无论如何(这是很好的从RAII观点) 这是有点邪恶,但是你可以提供这个
  4. 如上述代码,它甚至可以帮助检测指不指向一个有效的类

您应该做的关注点分离的位置:如果客户端通过在原始指针时,客户端将负责存储器管理(即清理之后)。如果您创建的指针,你将负责内存管理。这也将帮你是在另一个答案中提到的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 ++剃刀形的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top