C++ тилиндеги долбоорлоо үлгүлөрү
Классикалык үлгүлөр заманбап C++ тилинде кантип эрип, курчуп же формасын өзгөртөрү
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 болушу керек - каталарды жутуп же журналга жазыңыз, бирок аларды деструктордон эч качан чыгарбаңыз.