سؤال

هل هناك طريقة لتزيين يدويا وتناقص عدد المشاركين في C ++؟

المشكلة التي أحاول حلها هي كما يلي. أكتب مكتبة في C ++ ولكن الواجهة يجب أن تكون في نقية C. داخليا، أود استخدام Shared_Ptr لتبسيط إدارة الذاكرة مع الحفاظ على القدرة على اجتياز مؤشر RAW من خلال واجهة 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

بهذه الطريقة، عميلك "يقلل" العداد هو في الواقع عميل يدعو طريقة رد الاتصال مع المعرف الذي استخدمته، وتحذف One Shared_PTR :)

القرصنة دفعة :: Shared_ptr

كما قلت أنه من الممكن (لأننا في C ++) في الواقع اختراق في Shared_ptr. هناك عدة طرق للقيام بذلك.

ال أفضل الطريقة (وأسهل) هي ببساطة نسخ الملف لأسفل تحت اسم آخر (my_shared_ptr؟) ثم:

  • تغيير حراس تشمل
  • تشمل shared_ptr الحقيقي في البداية
  • إعادة تسمية أي مثيل من Shared_ptr باسمك الخاص (وتغيير القطاع الخاص إلى الجمهور للوصول إلى السمات)
  • قم بإزالة جميع الأشياء التي تم تعريفها بالفعل في الملف الحقيقي لتجنب الاشتباكات

وبهذه الطريقة يمكنك بسهولة الحصول على Shared_ptr الخاصة بك، والتي يمكنك الوصول إليها. لا يحل مشكلة وجود رمز C الوصول مباشرة إلى العداد، فقد تضطر إلى "تبسيط" الرمز هنا لاستبداله بواسطة مدمج (يعمل إذا لم تكن متعددة الخيوط، وهو كارثية بصراحة إذا كنت).

لقد تركت عمدا خدعة "إعادة تفسير" وخداع آراء المؤشر. هناك العديد من الطرق كثيرة لاكتساب الوصول إلى شيء ما في C / C ++!

هل لي أن أنصحك بعدم استخدام الخارقة؟ يجب أن يكون الحلول الذي قدمته أعلاه كافيا لمعالجة مشكلتك.

نصائح أخرى

ربما كنت تستخدم حدود Boost :: Shared_Ptr Accrosross DLL، ما لن يعمل بشكل صحيح. في هذه الحالة دفعة :: تدخلية_ptr. قد تساعدك. هذه حالة شائعة من سوء استخدام shared_ptr يحاول الأشخاص العمل مع الخارقة القذرة ... ربما أكون مخطئا في قضيتك ولكن يجب ألا يكون هناك سبب وجيه للقيام بما تحاول القيام به ؛-)

تمت إضافة 07/2010: يبدو أن المشكلات تأتي أكثر من تحميل DLL / التفريغ من Shared_Ptr نفسه. حتى الأساس المنطقي تعزيز لا يخبر الكثير عن الحالات التي boost::intrusive_ptr يجب أن يكون المفضل أكثر shared_ptr. وبعد لقد تحولت إلى تطوير .NET ولم اتبع تفاصيل TR1 المتعلقة بهذا الموضوع، لذلك احذر هذه الإجابة قد لا تكون صالحة بعد الآن ...

1. مقبض؟

إذا كنت تريد الحد الأقصى للأمان، فإنه يمنح المستخدم مقبضا وليس المؤشر. بهذه الطريقة، لا توجد طريقة سيحاول free ونجح نصف النجاح.

سأفترض أدناه ذلك، من أجل البساطة، ستقدم المستخدم مؤشر الكائنات.

2. اكتساب وغير المناعي؟

يجب عليك إنشاء فئة مدير، كما هو موضح بواسطة Matthieu M. في بلده إجابه, لحفظ ما تم الحصول عليه / غير مرغوب من قبل المستخدم.

كما inferface هو ج، لا يمكنك أن تتوقع منه استخدام 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) ;
}

}

لاحظ أن المؤشر إلى الحساب غير صالح عادي. يجب ألا تستخدمه لأي سبب من الأسباب دون إعادة تفسيرها - في نوع MyClass الأصلي (انظر Jaif إجابه لمزيد من المعلومات حول ذلك. سيستخدم المستخدم C فقط مع وظائف MACETY_ * المرتبطة.

لاحظ أيضا أن هذا الرمز يتحقق من فئة موجودة. قد يكون هذا مبالا، لكنه استخدام محتمل للمدير (انظر أدناه)

3. عن المدير

سيعقد المدير، كما اقترحه Matthieu M.، خريطة تحتوي على المؤشر المشترك كقيمة (والمؤشر نفسه، أو المقبض، مثل المفتاح). أو multimap، إذا كان ذلك ممكنا للمستخدم الحصول على نفس الكائن عدة مرات.

الشيء الجيد حول استخدام المدير سيكون هو أن رمز C ++ الخاص بك سيكون قادرا على تتبع الكائنات التي لم تكن "غير مرغوبة" بشكل صحيح من قبل المستخدم (إضافة معلومات في أساليب الحصول / غير المنقولة مثل __FILE__ و __LINE__ يمكن أن تساعد في تضييق البحث عن الأخطاء).

وبالتالي فإن المدير سيكون قادرا على:

  1. لا يوجد كائن غير موجود (كيف تمكن المستخدم C من الحصول على واحدة، بالمناسبة؟)
  2. تعرف في نهاية التنفيذ الذي لم يتم الافراج عن الكائنات
  3. في حالة objets غير النظير، قم بتدميرها على أي حال (وهو أمر جيد من وجهة نظر راي) هذا شريرا إلى حد ما، ولكن يمكنك تقديم هذا
  4. كما هو موضح في التعليمات البرمجية أعلاه، يمكن أن يساعد حتى الكشف عن مؤشر لا يشير إلى فئة صالحة

يجب عليك فصل الشواغل هنا: إذا مر العميل في مؤشر RAW، فسيكون العميل مسؤولا عن إدارة الذاكرة (أي التنظيف بعد ذلك). إذا قمت بإنشاء المؤشرات، فستكون مسؤولا عن إدارة الذاكرة. سيؤدي ذلك أيضا إلى مساعدتك في مشكلات حدود DLL التي تم ذكرها في إجابة أخرى.

جئت عبر قضية استخدام حيث كنت بحاجة إلى شيء من هذا القبيل، وترتبط ب IOCOMPLETIONPORTS ومخاوف التزامن. المخرجات ولكن الطريقة المتوافقة مع المعايير هي محامي كما هو موضح بواسطة عشبة سوتر هنا.

مقتطف التعليمات البرمجية التالي هو STD :: Shared_ptr كما تم تطبيقه بواسطة VC11:

إمضاع الملف:

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، من أجل زيادة إعادة المرجعين، ويكاملها من أجل تقليلها. هذه الضمانات أن كائن مشترك لن يتم تدميره أثناء الاستخدام من قبل عميل API C.

في مقتطف التعليمات البرمجية التالي، أستخدم الزيادة () وانخفاض () من أجل التحكم في 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