ما فئة المجمع في C ++ يجب أن أستخدمها لإدارة الموارد الآلية؟

StackOverflow https://stackoverflow.com/questions/2433240

  •  19-09-2019
  •  | 
  •  

سؤال

أنا هواة C ++. أنا أكتب بعض كود Win32 API وهناك مقابض وغرابة كائنات مخصصة بشكل غريب APLENT. لذلك كنت أتساءل - هل هناك بعض فئة المجمع من شأنها أن تجعل إدارة الموارد أسهل؟

على سبيل المثال، عندما أرغب في تحميل بعض البيانات فأي افتح ملف CreateFile() والحصول على HANDLE. وبعد عندما انتهيت منه، يجب أن أتصل CloseHandle() عليه. ولكن بالنسبة لأي وظيفة تحميل معقدة معقدة، ستكون هناك عشرات نقاط الخروج المحتملة، ناهيك عن الاستثناءات.

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

المفهوم بسيط - ولكن هل هناك شيء من هذا القبيل في المكتبة القياسية؟ أنا أستخدم Visual Studio 2008، بالمناسبة، ولا أريد إرفاق إطار طرفي ثالث مثل دفعة أو شيء ما.

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

المحلول

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

struct FileWrapper {
  FileWrapper(...) : h(CreateFile(...)) {}
  ~FileWrapper() { CloseHandle(h); }

private:
  HANDLE h;
};

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

بالطبع، قد يسلم C ++ 0x التوازن إلى حد ما مع إضافة تعبيرات Lambda. يمكن بسهولة تمرير تعبيرات لامهة إلى فئة مجمع عام، لذلك بمجرد أن يأتي دعم C ++ 0x، نحن ربما انظر مثل هذه الطبقة الرايمية العامة تضاف إلى تعزيز أو شيء ما.

ولكن في الوقت الحالي، من الأسهل فقط لفة خاصة بك كلما احتجت إليها.

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

اذا أنت فعل بحاجة الى عد النقاش، فقط تفعل شيئا مثل boost::shared_ptr<FileWrapper>: لف فصول راي مخصصة مخصصة الخاص بك في shared_ptr.

نصائح أخرى

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

ومع ذلك، fstream يعمل فقط من أجل الملفات، وليس من أجل مقابض عامة، أي خيوط، العمليات، وكائنات المزامنة، ملفات المعينة بالذاكرة، إلخ.

وتسمى هذه الأغلفة ATL.

إذا كان مقبضك حدثا أو ما شابه ذلك، فاستخدم قندلي صف دراسي.

إذا كان المقبض الخاص بك هو ملف، استخدم CatlFile مشتق من واحد، فهو يلف واجهات برمجة التطبيقات مثل Createfile and ReadFile.

هناك مغلفة مفيدة أخرى في ATL، CAtlFileMapping<T> هو غلاف راي على الملفات المعينة بالذاكرة، CPath يلف apis shell32 لمعالجة المسار، وهلم جرا.

ATL هي مكتبة كبيرة، ولكن أشياء منخفضة المستوى مثل الملفات والسلاسل والمجموعات معزولة. يمكنك استخدامها في جميع تطبيقات Win32. هو الرأس فقط، لا تحتاج إلى الارتباط بأي شيء، أو توزيع DLL إضافية مثل MFC أو CRT، فإن التعليمات البرمجية تحتوي على مكالمات Winapi ويعمل فقط.

تم تقسيمهم من MFC في VS2003 أو 2005، لا تتذكر، أي Visual Studio 2008 بالتأكيد لديه لهم. ومع ذلك، هناك تحذير واحد، إذا كنت تستخدم نسخة مجانية من VS، يجب أن يكون عام 2015 أو أحدث.

إليك واحدة واحدة تقوم بإيقاف رمز Entercleanup من "Windows عبر C / C ++:http://www.codeproject.com/kb/cpp/template2003.aspx.

MFC لديها بعض البدائيات المناسبة (انظروا إلى cfile. على سبيل المثال)، ولكن ليس المكتبة القياسية.

يدعم Visual C ++ 2008 TR1 من خلال حزمة الميزة، وتشمل TR1 Shared_Ptr. كنت أستخدم هذا - إنها فئة مؤشر ذكية قوية للغاية ويمكن تعميمها للقيام بنوع إدارة الموارد التي تطلبها.

TR1 هو امتداد فعال للمعايير. أعتقد أنه لا يزال "ما قبل القياسية" رسميا، ولكن بفعالية يمكنك التفكير في أنه مغلق.

لا أعتقد أن هناك أي شيء في المكتبة القياسية، وأشك أيضا في أنه يمكن استخدام المؤشرات المشتركة (كما هو الحال في دفعة) (نظرا لأن تلك تتوقع مؤشر التعامل معها، وليس مقبض).

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

template <typename Traits>
class unique_handle
{
    using pointer = typename Traits::pointer;

    pointer m_value;

    auto close() throw() -> void
    {
        if (*this)
        {
            Traits::close(m_value);
        }
    }

public:

    unique_handle(unique_handle const &) = delete;
    auto operator=(unique_handle const &)->unique_handle & = delete;

    explicit unique_handle(pointer value = Traits::invalid()) throw() :
        m_value{ value }
    {
    }

    unique_handle(unique_handle && other) throw() :
        m_value{ other.release() }
    {
    }

    auto operator=(unique_handle && other) throw() -> unique_handle &
    {
        if (this != &other)
        {
            reset(other.release());
        }

        return *this;
    }

    ~unique_handle() throw()
    {
        close();
    }

    explicit operator bool() const throw()
    {
        return m_value != Traits::invalid();
    }

    auto get() const throw() -> pointer
    {
        return m_value;
    }

    auto get_address_of() throw() -> pointer *
    {
        ASSERT(!*this);
        return &m_value;
    }

    auto release() throw() -> pointer
    {
        auto value = m_value;
        m_value = Traits::invalid();
        return value;
    }

    auto reset(pointer value = Traits::invalid()) throw() -> bool
    {
        if (m_value != value)
        {
            close();
            m_value = value;
        }

        return static_cast<bool>(*this);
    }

    auto swap(unique_handle<Traits> & other) throw() -> void
    {
        std::swap(m_value, other.m_value);
    }
};

template <typename Traits>
auto swap(unique_handle<Traits> & left,
    unique_handle<Traits> & right) throw() -> void
{
    left.swap(right);
}

template <typename Traits>
auto operator==(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() == right.get();
}

template <typename Traits>
auto operator!=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() != right.get();
}

template <typename Traits>
auto operator<(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() < right.get();
}

template <typename Traits>
auto operator>=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() >= right.get();
}

template <typename Traits>
auto operator>(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() > right.get();
}

template <typename Traits>
auto operator<=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() <= right.get();
}

struct null_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return nullptr;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

struct invalid_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return INVALID_HANDLE_VALUE;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

using null_handle = unique_handle<null_handle_traits>;
using invalid_handle = unique_handle<invalid_handle_traits>;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top