C 11 ScopeGuard Paling Mudah dan Kemas
Dalam C 11, terdapat simpulan bahasa mudah untuk melaksanakan ScopeGuard yang memudahkan ralat dan pengendalian sumber. Berikut ialah penjelasan ringkas dan pelaksanaan:
Konsep:
ScopeGuard ialah kelas C yang menyediakan cara untuk menentukan blok kod yang akan dilaksanakan secara automatik apabila skop di mana pengawal telah dicipta keluar. Ini membolehkan pembersihan mudah dan pengendalian ralat, memastikan sumber dikeluarkan atau tindakan diambil walaupun dalam keadaan luar biasa.
Pelaksanaan:
<code class="cpp">namespace RAII { template< typename Lambda > class ScopeGuard { mutable bool committed; Lambda rollbackLambda; public: ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {} template< typename AdquireLambda > ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l) { _al(); } ~ScopeGuard() { if (!committed) rollbackLambda(); } inline void commit() const { committed = true; } }; template< typename aLambda , typename rLambda> const ScopeGuard< rLambda >& makeScopeGuard( const aLambda& _a , const rLambda& _r) { return ScopeGuard< rLambda >( _a , _r ); } template<typename rLambda> const ScopeGuard< rLambda >& makeScopeGuard(const rLambda& _r) { return ScopeGuard< rLambda >(_r ); } }</code>
Penggunaan :
<code class="cpp">void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptions() { std::vector<int> myVec; std::vector<int> someOtherVec; myVec.push_back(5); //first constructor, adquire happens elsewhere const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } ); //sintactically neater, since everything happens in a single line const auto& b = RAII::makeScopeGuard( [&]() { someOtherVec.push_back(42); } , [&]() { someOtherVec.pop_back(); } ); b.commit(); a.commit(); }</code>
Dalam contoh ini, simpulan bahasa ScopeGuard digunakan untuk memastikan tindakan pembersihan (elemen pop dari vektor) dilaksanakan walaupun pengecualian dilemparkan dalam skop fungsi.
Kesederhanaan dan Had:
Pelaksanaan simpulan bahasa ScopeGuard ini agak mudah dan ringkas, menjadikannya mudah digunakan dan difahami. Walau bagaimanapun, pengarang soalan asal juga menimbulkan kebimbangan mengenai potensi kekurangan atau kepakaran yang hilang. Mari kita atasi:
Untuk menangani kebimbangan ini, berikut ialah versi terkini pelaksanaan dan penggunaan ScopeGuard:
<code class="cpp">namespace RAII { template< typename Lambda > class ScopeGuard { bool committed; Lambda rollbackLambda; public: ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {} ScopeGuard( const ScopeGuard& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda) { if (_sc.committed) committed = true; else _sc.commit(); } ScopeGuard( ScopeGuard&& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda) { if (_sc.committed) committed = true; else _sc.commit(); } //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion.. template< typename AdquireLambda > ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l) { std::forward<AdquireLambda>(_al)(); } //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion.. template< typename AdquireLambda, typename L > ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l)) { std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator() } ~ScopeGuard() { if (!committed) rollbackLambda(); } inline void commit() { committed = true; } }; //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion.. template< typename aLambda , typename rLambda> ScopeGuard< rLambda > // return by value is the preferred C++11 way. makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding { return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value } template< typename aLambda , typename rLambda> ScopeGuard< rLambda > // return by value is the preferred C++11 way. makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding { auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value _a(); return scope; } template<typename rLambda> ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r) { return ScopeGuard< rLambda >( std::forward<rLambda>(_r )); } namespace basic_usage { struct Test { std::vector<int> myVec; std::vector<int> someOtherVec; bool shouldThrow; void run() { shouldThrow = true; try { SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows(); } catch (...) { AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work"); } shouldThrow = false; SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows(); AssertMsg( myVec.size() == 1 && someOtherVec.size() == 1 , "unexpected end state"); shouldThrow = true; myVec.clear(); someOtherVec.clear(); try { SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows(); } catch (...) { AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work"); } } void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows() //throw() { myVec.push_back(42);</code>
Atas ialah kandungan terperinci Berikut ialah beberapa pilihan tajuk, setiap satu menekankan aspek artikel yang berbeza: Pilihan 1: Memfokuskan pada konsep dan ciri C 11: * ScopeGuard dalam C 11: Pengendalian Ralat Mudah, Tetapi Yang Mengira. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!