سؤال

هل يمكن تعزيز ::shared_ptr تحرير المؤشر المخزن دون حذفه؟

لا أستطيع أن أرى أي وظيفة إصدار موجودة في الوثائق، وأيضًا في الأسئلة الشائعة تم شرح سبب عدم توفير وظيفة الإصدار، شيء من هذا القبيل لا يمكن تنفيذ الإصدار على مؤشرات ليست فريدة من نوعها.مؤشراتي فريدة من نوعها.كيف يمكنني تحرير المؤشرات الخاصة بي؟أو ما هي فئة المؤشر الذكي التي يجب استخدامها والتي ستسمح لي بتحرير المؤشر؟أتمنى ألا تقول استخدم auto_ptr :)

هل كانت مفيدة؟

المحلول

وتحتاج إلى استخدام deleter التي يمكنك طلب عدم حذف المؤشر الأساسي.

انظر هذه الإجابة (والتي كانت وضع علامة على أنه نسخة مكررة من هذا السؤال) لمزيد من المعلومات.

نصائح أخرى

لا.إدخال الأسئلة الشائعة الخاصة بـ Boost:

س.لماذا لا يوفر Shared_ptr وظيفة الإصدار ()؟

أ. Shared_ptr لا يمكن التخلي عن الملكية إلا إذا كانت فريدة () لأن النسخة الأخرى ستستمر في تدمير الكائن.

يعتبر:

shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2

int * p = a.release();

// Who owns p now? b will still call delete on it in its destructor.

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

لذلك، سيكون هذا آمنًا في حالة كون المثيل Shared_ptr الوحيد الذي يشير إلى الكائن الخاص بك (عندما يعود Unique() صحيحًا) ولا يتطلب الكائن أداة حذف خاصة.ما زلت أتساءل عن التصميم الخاص بك، إذا كنت تستخدم مثل هذه الوظيفة .release().

هل يمكن استخدام deleter وهمية. ثم لن يتم حذف مؤشرات الواقع.

struct NullDeleter {template<typename T> void operator()(T*) {} };

// pp of type some_t defined somewhere
boost::shared_ptr<some_t> x(pp, NullDeleter() );

وأطفال، لا تفعل ذلك في المنزل:

// set smarty to point to nothing
// returns old(smarty.get())
// caller is responsible for the returned pointer (careful)
template <typename T>
T* release (shared_ptr<T>& smarty) {
    // sanity check:
    assert (smarty.unique());
    // only one owner (please don't play games with weak_ptr in another thread)
    // would want to check the total count (shared+weak) here

    // save the pointer:
    T *raw = &*smarty;
    // at this point smarty owns raw, can't return it

    try {
        // an exception here would be quite unpleasant

        // now smash smarty:
        new (&smarty) shared_ptr<T> ();
        // REALLY: don't do it!
        // the behaviour is not defined!
        // in practice: at least a memory leak!
    } catch (...) {
        // there is no shared_ptr<T> in smarty zombie now
        // can't fix it at this point:
        // the only fix would be to retry, and it would probably throw again
        // sorry, can't do anything
        abort ();
    }
    // smarty is a fresh shared_ptr<T> that doesn't own raw

    // at this point, nobody owns raw, can return it
    return raw;
}

والآن، هل هناك طريقة لمعرفة ما اذا كان العدد الإجمالي لأصحاب الأولوية القصوى للمحافظة العد غير> 1

لترك نقطة المؤشر إلى شيء مرة أخرى، يمكنك الاتصال shared_ptr::reset().

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

إذا كنت ترغب فقط في المرجعية التي لا يحمل الكائن على قيد الحياة، يمكنك إنشاء boost::weak_ptr (انظر <لأ href = "http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/weak_ptr . HTM "يختلط =" noreferrer نوفولو "> دفعة وثائق ). A weak_ptr يحمل إشارة إلى كائن ولكن لا يضيف إلى عدد مرجع، بحيث يحصل حذف الكائن عند وجود إلا إشارات ضعيفة.

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

ولكن، في الآونة الأخيرة أردت أن تفعل ذلك أيضا، وأنا بحاجة إلى إلغاء تخصيص من مختلف عملية الكومة. في النهاية تعلمت أن قراري كبار السن لاستخدام بعض std::shared_ptr لم مدروسة.

وأنا تستخدم فقط بشكل روتيني هذا النوع لتنظيف. لكن المؤشر كان تكرار فقط على عدد قليل من الأماكن. في الواقع أنا في حاجة إلى std::unique_ptr، والتي (مفاجأة) لديه وظيفة release.

واغفر لهم لأنهم لا يدرون ماذا يفعلون. هذا المثال يعمل مع دفعة :: shared_ptr وmsvs الأمراض المنقولة جنسيا :: shared_ptr دون تسرب الذاكرة!

template <template <typename> class TSharedPtr, typename Type>
Type * release_shared(TSharedPtr<Type> & ptr)
{
    //! this struct mimics the data of std:shared_ptr ( or boost::shared_ptr )
    struct SharedVoidPtr
    {
        struct RefCounter
        {
            long _Uses;
            long _Weaks;
        };

        void * ptr;
        RefCounter * refC;

        SharedVoidPtr()
        {
            ptr = refC = nullptr;
        }

        ~SharedVoidPtr()
        {
            delete refC;
        }
    };

    assert( ptr.unique() );

    Type * t = ptr.get();

    SharedVoidPtr sp; // create dummy shared_ptr
    TSharedPtr<Type> * spPtr = (TSharedPtr<Type>*)( &sp );
    spPtr->swap(ptr); // swap the contents

    ptr.reset();
    // now the xxx::shared_ptr is empy and
    // SharedVoidPtr releases the raw poiter but deletes the underlying counter data
    return t;
}

ويمكنك حذف المؤشر المشتركة، والتي يبدو الى حد كبير نفس لي. إذا مؤشرات هي دائما فريدة من نوعها، ثم std::auto_ptr<> هو خيار جيد. نضع في اعتبارنا أن مؤشرات فريدة من نوعها لا يمكن أن تستخدم في حاويات STL، منذ العمليات عليها تفعل الكثير من نسخ والازدواجية المؤقتة.

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

void print(std::string name, std::shared_ptr<std::unique_ptr<int>>& ptr)
{
    if(ptr == nullptr || *ptr == nullptr)
    {
        std::cout << name << " points to nullptr" << std::endl;
    }
    else
    {
        std::cout << name << " points to value " << *(*ptr) << std::endl;
    }
}

int main()
{
    std::shared_ptr<std::unique_ptr<int>> original;
    original = std::make_shared<std::unique_ptr<int>>(std::make_unique<int>(50));

    std::shared_ptr<std::unique_ptr<int>> shared_original = original;

    std::shared_ptr<std::unique_ptr<int>> thief = nullptr;

    print(std::string("original"), original);
    print(std::string("shared_original"), shared_original);
    print(std::string("thief"), thief);

    thief = std::make_shared<std::unique_ptr<int>>(original->release());

    print(std::string("original"), original);
    print(std::string("shared_original"), shared_original);
    print(std::string("thief"), thief);

    return 0;
}

وإخراج:

original points to value 50
shared_original points to value 50
thief points to nullptr
original points to nullptr
shared_original points to nullptr
thief points to value 50

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

وهنا الإختراق الذي عمل القوة. وأود أن لا أوصي به إلا إذا كنت في مأزق حقيقي.

template<typename T>
T * release_shared(std::shared_ptr<T> & shared)
{
    static std::vector<std::shared_ptr<T> > graveyard;
    graveyard.push_back(shared);
    shared.reset();
    return graveyard.back().get();
}

إذا المؤشرات الخاصة بك هي في الواقع تأليف فريد استخدام std::unique_ptr أو boost::scoped_ptr إذا كانت الأولى ليست متاحة للمترجم الخاص بك. تنظر إلا الجمع بين استخدام boost::shared_ptr مع boost::weak_ptr. تحقق من دفعة وثائق للحصول على مزيد من التفاصيل.

وأستخدمه بوكو :: HTTPRequestHandlerFactory التي تتوقع لإرجاع الخام HTTPRequestHandler *، إطار بوكو حذف معالج بمجرد انتهاء الطلب.

وأيضا باستخدام مشروع DI صلصة لإنشاء وحدات تحكم، ولكن الحاقن يعود shared_ptr التي لا أستطيع العودة مباشرة، وإعادة handler.get () لم جيدة سواء منذ حالما ترجع هذه الدالة shared_ptr يخرج من نطاق و حذف ثم معالج قبل تنفيذها، لذلك هنا هو معقول (على ما أظن) سبب لوجود طريقة .release (). انتهى بي الأمر خلق طبقة HTTPRequestHandlerWrapper على النحو التالي: -

class HTTPRequestHandlerWrapper : public HTTPRequestHandler {
private:
    sauce::shared_ptr<HTTPRequestHandler> _handler;

public:
    HTTPRequestHandlerWrapper(sauce::shared_ptr<HTTPRequestHandler> handler) {
        _handler = handler;
    }

    virtual void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) {
        return _handler->handleRequest(request, response);
    }
};

وثم المصنع سوف

HTTPRequestHandler* HttpHandlerFactory::createRequestHandler(const HTTPServerRequest& request) {
    URI uri = URI(request.getURI());
    auto path = uri.getPath();
    auto method = request.getMethod();

    sauce::shared_ptr<HTTPRequestHandler> handler = _injector->get<HTTPRequestHandler>(method + ":" + path);

    return new HTTPRequestHandlerWrapper(handler);
}

والتي استوفت كل من صلصة وبوكو ويعمل بشكل جيد.

وأنا بحاجة لتمرير مؤشر من خلال معالجات المتزامن وللحفاظ على سلوك التدمير الذاتي في حالة الفشل، ولكن توقع API النهائي مؤشر الخام، لذلك جعل هذه الوظيفة لإطلاق سراح من shared_ptr واحد:

#include <memory>

template<typename T>
T * release(std::shared_ptr<T> & ptr)
{
    struct { void operator()(T *) {} } NoDelete;

    T * t = nullptr;
    if (ptr.use_count() == 1)
    {
        t = ptr.get();
        ptr.template reset<T>(nullptr, NoDelete);
    }
    return t;
}

إذا ptr.use_count() != 1 يجب عليك الحصول على nullptr بدلا من ذلك.

وسهلة الحل، وزيادة إشارة وثم تسرب shared_pointer.

boost::shared_ptr<MyType> shared_pointer_to_instance(new MyType());
new boost::shared_ptr<MyType>();
MyType * raw_pointer = shared_pointer_to_instance.get()

وهذا سوف يسبب بوضوح حدوث تسرب للذاكرة كل من shared_ptr وMyType *

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top