Мазмунга өтүү

C++ тилиндеги долбоорлоо үлгүлөрү

Сабактын башталышы

C++ тилиндеги долбоорлоо үлгүлөрү

Классикалык үлгүлөр заманбап C++ тилинде кантип эрип, курчуп же формасын өзгөртөрү

Негизги идея: "Төрттүктүн бандасынын" үлгүлөрү RAII, шаблондор, лямбдалар жана жылдыруу семантикасы жок тил үчүн жазылган. Заманбап C++ тилинде (C++17/20) алардын көбү тилдин өзү менен кайра формаланат: кол менен тазалоо RAII болуп калат, virtual аркылуу динамикалык диспетчерлөө көбүнчө компиляция учурундагы CRTP менен алмашат, аталыш файлдарынын көз карандылыгы pimpl идиомасы менен үзүлөт, а Strategy менен Observer болсо std::function жана лямбдага чейин кичирейет. Үлгү - бул чечимдин формасы, сөзмө-сөз көчүрүлүүчү класстардын иерархиясы эмес.
Аягында сиз төмөнкүлөрдү жасай аласыз:
  • каалаган ресурсту RAII-орогучка айландыруу жана универсалдуу scope guard жазуу;
  • CRTP кошумча чыгымсыз статикалык полиморфизм жана миксиндер үчүн колдонуу;
  • pimpl идиомасын компиляция тосмосун жана туруктуу ABI куруу үчүн колдонуу;
  • Strategy жана Observer үлгүлөрүн std::function, лямбдалар жана weak_ptr аркылуу ишке ашыруу;
  • ар бир тандоонун компромисстерин чынчылдык менен таразалоо (кошумча чыгым, байланыштуулук, ийкемдүүлүк).

1. RAII - үлгүлөрдүн генератору катары (scope guard)

Сиз RAII ти эстутум (unique_ptr), кулпулар (std::lock_guard) жана файлдар (std::fstream) үчүн буга чейин колдонуп жатасыз. RAII - бул бир үлгү эмес, а үлгүлөрдүн фабрикасы: алуу жана бошотуу кадамдары бар каалаган ресурсту, бошотуу деструктордо ар бир чыгуу жолунда, анын ичинде өзгөчө кырдаалда стектин чечилишинде да автоматтык түрдө болушу үчүн орогочко салса болот.

Даяр орогуч жок болгондо, scope guard RAII ти каалаган тазалоону көрүнүү аймагынын аягында аткаруу үчүн жалпылайт. Ал чакырылуучу объектти сактап, аны деструкторунда чакырат, эгер сиз аны алдын ала dismiss() аркылуу жокко чыгарбасаңыз (commit/rollback ийгилик жолу үчүн):

template <class F>
class ScopeGuard {
    F cleanup_;
    bool active_ = true;
public:
    explicit ScopeGuard(F f) : cleanup_(std::move(f)) {}
    void dismiss() noexcept { active_ = false; }
    ~ScopeGuard() { if (active_) cleanup_(); }   // ар кандай чыгуу жолунда аткарылат

    ScopeGuard(const ScopeGuard&) = delete;       // көчүрүлбөйт
    ScopeGuard& operator=(const ScopeGuard&) = delete;
};

template <class F>
ScopeGuard<F> makeGuard(F f) { return ScopeGuard<F>(std::move(f)); }

Колдонулушу кол менен жазылган finally сыяктуу окулат, бирок өзгөчө кырдаалдарда коопсуз жана try/catch ты талап кылбайт:

FILE* f = std::fopen("data.bin", "rb");
auto guard = makeGuard([&]{ if (f) std::fclose(f); });   // ар дайым жабат
// ... f ти колдонобуз; эгер бул жерде бир нерсе ыргытса, guard баары бир файлды жабат ...

C++20 / китепкана боюнча эскертүү: ушул эле идея Library Fundamentals TS те std::experimental::scope_exit катары стандартташтырылган жана GSL де gsl::finally катары бар. Жеткиликтүү болгондо аларды өзүңүздүн ишке ашырууңуздан артык көрүңүз.

Чынчыл эскертүү: стек чечилип жатканда өзгөчө кырдаал ыргыткан деструктор std::terminate ти чакырат. Тазалоо лямбдалары маңызы боюнча noexcept болушу керек - каталарды жутуп же журналга жазыңыз, бирок аларды деструктордон эч качан чыгарбаңыз.

Бул сабакты YourAIZone платформасында улантыңыз

Толук сабакта — толук түшүндүрмө, чыгарылган мисалдар, көп кездешкен каталар, заматта текшерилүүчү тесттер жана ЖИ-репетитор. Конок катары дароо баштаңыз же прогрессти сактоо үчүн аккаунт түзүңүз.

Толук сабакта

  • Толук түшүндүрмө
  • Чыгарылган мисалдар
  • Көп кездешкен каталар жана алардан кантип качуу керек
  • Тест суроолору: 15
  • Тема боюнча суроолорго жооп берген ЖИ-репетитор
  • Окуучу, ата-эне жана мугалим үчүн прогрессти көзөмөлдөө