क्या C ++ ' अंततः ' ब्लॉक का समर्थन करता है ?
RAII मुहावरा क्या है ?
C ++ के RAII मुहावरे और C # 's' कथन के बीच अंतर क्या है ?
क्या C ++ ' अंततः ' ब्लॉक का समर्थन करता है ?
RAII मुहावरा क्या है ?
C ++ के RAII मुहावरे और C # 's' कथन के बीच अंतर क्या है ?
जवाबों:
नहीं, C ++ 'अंततः' ब्लॉक का समर्थन नहीं करता है। "संसाधन अधिग्रहण है प्रारंभ" - एक: कारण यह है कि सी ++ के बजाय आरए II का समर्थन करता है है गरीब नाम † एक बहुत उपयोगी अवधारणा के लिए।
विचार यह है कि संसाधनों को मुक्त करने के लिए एक वस्तु का विनाशकर्ता जिम्मेदार है। जब ऑब्जेक्ट में स्वचालित भंडारण अवधि होती है, तो ऑब्जेक्ट के विनाशकर्ता को तब बुलाया जाएगा जब ब्लॉक जिसमें इसे बाहर निकाला गया था - तब भी जब वह ब्लॉक अपवाद की उपस्थिति में बाहर निकलता है। यहाँ Bjarne Stroustrup विषय की व्याख्या है।
RAII के लिए एक सामान्य उपयोग म्यूटेक्स को लॉक कर रहा है:
// A class with implements RAII
class lock
{
mutex &m_;
public:
lock(mutex &m)
: m_(m)
{
m.acquire();
}
~lock()
{
m_.release();
}
};
// A class which uses 'mutex' and 'lock' objects
class foo
{
mutex mutex_; // mutex for locking 'foo' object
public:
void bar()
{
lock scopeLock(mutex_); // lock object.
foobar(); // an operation which may throw an exception
// scopeLock will be destructed even if an exception
// occurs, which will release the mutex and allow
// other functions to lock the object and run.
}
};
RAII अन्य वर्गों के सदस्यों के रूप में वस्तुओं का उपयोग करने को भी सरल बनाता है। जब मालिक वर्ग 'को नष्ट कर दिया जाता है, तो RAII वर्ग द्वारा प्रबंधित संसाधन मुक्त हो जाता है क्योंकि RAII-प्रबंधित वर्ग के लिए विध्वंसक को परिणाम के रूप में कहा जाता है। इसका मतलब यह है कि जब आप संसाधनों का प्रबंधन करने वाले वर्ग में सभी सदस्यों के लिए RAII का उपयोग करते हैं, तो आप बहुत ही सरल का उपयोग करके दूर हो सकते हैं, शायद मालिक वर्ग के लिए डिफ़ॉल्ट, विध्वंसक भी क्योंकि इसके सदस्य संसाधन जीवनकाल को मैन्युअल रूप से प्रबंधित करने की आवश्यकता नहीं है । ( इसे इंगित करने के लिए माइक बी का धन्यवाद ।)
C # या VB.NET वाले उन पारिवारिक लोगों के लिए, आप पहचान सकते हैं कि RAII .NET नियतांक के रूप में आईडीआईएसओपी और 'स्टेटमेंट' का उपयोग करके विनाशकारी विनाश के समान है । वास्तव में, दो विधियां बहुत समान हैं। मुख्य अंतर यह है कि RAII किसी भी प्रकार के संसाधन को निर्धारित करेगा - जिसमें मेमोरी भी शामिल है। .NET (यहां तक कि .NET लैंग्वेज C ++ / CLI) में भी आईडीआईसपोजेबल लागू करते समय, संसाधनों को मेमोरी के अलावा निर्दिष्ट रूप से जारी किया जाएगा। .NET में, मेमोरी नियत रूप से जारी नहीं की जाती है; मेमोरी केवल कचरा संग्रह चक्र के दौरान जारी की जाती है।
† कुछ लोगों का मानना है कि "विनाश संसाधन संसाधन है" RAII मुहावरे का अधिक सटीक नाम है।
C ++ में RAII के कारण अंत में आवश्यक नहीं है।
RAII ऑब्जेक्ट के उपयोगकर्ता से ऑब्जेक्ट के डिज़ाइनर (और कार्यान्वयनकर्ता) तक अपवाद सुरक्षा की जिम्मेदारी ले जाता है। मेरा तर्क है कि यह सही जगह है क्योंकि आपको केवल एक बार (डिजाइन / कार्यान्वयन में) अपवाद सुरक्षा को सही करने की आवश्यकता है। अंत में किसी वस्तु का उपयोग करने पर आपको हर बार अपवाद सुरक्षा सही प्राप्त करने की आवश्यकता होती है।
इसके अलावा IMO कोड निचे दिखता है (नीचे देखें)।
उदाहरण:
एक डेटाबेस ऑब्जेक्ट। यह सुनिश्चित करने के लिए कि DB कनेक्शन का उपयोग किया जाता है, इसे खोला और बंद किया जाना चाहिए। RAII का उपयोग करके इसे कंस्ट्रक्टर / डिस्ट्रक्टर में किया जा सकता है।
void someFunc()
{
DB db("DBDesciptionString");
// Use the db object.
} // db goes out of scope and destructor closes the connection.
// This happens even in the presence of exceptions.
RAII का उपयोग किसी DB ऑब्जेक्ट का सही ढंग से उपयोग करना बहुत आसान बनाता है। डीबी ऑब्जेक्ट सही ढंग से एक विध्वंसक के उपयोग से खुद को बंद कर देगा चाहे हम इसे कैसे भी प्रयास करें और दुरुपयोग करें।
void someFunc()
{
DB db = new DB("DBDesciptionString");
try
{
// Use the db object.
}
finally
{
// Can not rely on finaliser.
// So we must explicitly close the connection.
try
{
db.close();
}
catch(Throwable e)
{
/* Ignore */
// Make sure not to throw exception if one is already propagating.
}
}
}
अंत में उपयोग करने पर वस्तु का सही उपयोग वस्तु के उपयोगकर्ता को सौंप दिया जाता है। अर्थात यह डीबी कनेक्शन को स्पष्ट रूप से बंद करने के लिए ऑब्जेक्ट उपयोगकर्ता की जिम्मेदारी है। अब आप यह तर्क दे सकते हैं कि यह अंतिम रूप से किया जा सकता है, लेकिन संसाधनों की सीमित उपलब्धता या अन्य बाधाएँ हो सकती हैं और इस प्रकार आप आम तौर पर ऑब्जेक्ट की रिलीज़ को नियंत्रित करना चाहते हैं और कचरा संग्रहकर्ता के गैर निर्धारक व्यवहार पर भरोसा नहीं करते हैं।
साथ ही यह एक सरल उदाहरण है।
जब आपके पास कई संसाधन हैं जिन्हें जारी करने की आवश्यकता है तो कोड जटिल हो सकता है।
अधिक विस्तृत विश्लेषण यहां पाया जा सकता है: http://accu.org/index.php/journals/236
// Make sure not to throw exception if one is already propagating.
C ++ डिस्ट्रक्टर्स के लिए अपवादों को फेंकना महत्वपूर्ण है और साथ ही इस कारण से महत्वपूर्ण है।
आरएआई आमतौर पर बेहतर होता है, लेकिन आप आसानी से सी ++ में अंतिम शब्दार्थ को आसानी से प्राप्त कर सकते हैं । कोड की एक छोटी राशि का उपयोग करना।
इसके अलावा, C ++ कोर दिशानिर्देश अंत में देते हैं।
यहाँ जीएसएल Microsoft कार्यान्वयन और मार्टिन मोइने कार्यान्वयन के लिए एक कड़ी है
बज़्ने स्ट्रॉस्ट्रुप ने कई बार कहा कि जीएसएल में वह सब कुछ है जो अंततः मानक में जाने का था। तो यह अंत में उपयोग करने के लिए एक भविष्य प्रूफ तरीका होना चाहिए ।
आप आसानी से अपने आप को लागू कर सकते हैं यदि आप चाहें, तो पढ़ना जारी रखें।
C ++ 11 RAII और लैम्ब्डा में अंत में एक सामान्य बनाने की अनुमति देता है:
namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
FinalAction(F f) : clean_{f} {}
~FinalAction() { if(enabled_) clean_(); }
void disable() { enabled_ = false; };
private:
F clean_;
bool enabled_{true}; }; }
template <typename F>
detail::FinalAction<F> finally(F f) {
return detail::FinalAction<F>(f); }
उपयोग का उदाहरण:
#include <iostream>
int main() {
int* a = new int;
auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
std::cout << "doing something ...\n"; }
उत्पादन होगा:
doing something...
leaving the block, deleting a!
व्यक्तिगत रूप से मैंने C ++ प्रोग्राम में POSIX फाइल डिस्क्रिप्टर को बंद करने के लिए कुछ समय का उपयोग किया।
एक वास्तविक वर्ग का होना जो संसाधनों का प्रबंधन करता है और इसलिए किसी भी तरह के लीक से बचा जाता है, आमतौर पर बेहतर होता है, लेकिन यह अंत में उन मामलों में उपयोगी होता है, जहां कक्षा को ओवरकिल की तरह लगता है।
इसके अलावा, मुझे अन्य भाषाओं की तुलना में यह बेहतर लगता है कि अंत में क्योंकि अगर स्वाभाविक रूप से आप शुरुआती कोड के पास समापन कोड लिखते हैं (मेरे उदाहरण में नया और हटाएं ) और विनाश C ++ में हमेशा की तरह LIFO ऑर्डर में निर्माण का अनुसरण करता है। केवल नकारात्मक पक्ष यह है कि आपको एक ऑटो वैरिएबल मिलता है जिसका आप वास्तव में उपयोग नहीं करते हैं और लैम्ब्डा सिंटैक्स इसे थोड़ा शोर बनाते हैं (मेरे उदाहरण में चौथी पंक्ति में केवल शब्द अंत में और {} -बोल दाईं ओर अर्थपूर्ण हैं, सार्थक बाकी अनिवार्य रूप से शोर है)।
एक और उदाहरण:
[...]
auto precision = std::cout.precision();
auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
std::cout << std::setprecision(3);
अक्षम अगर सदस्य उपयोगी है अंत में केवल विफलता के मामले में कहा जाता है किया जाना है। उदाहरण के लिए, आप आप सेटअप कर सकते हैं तीन अलग-अलग कंटेनर में एक वस्तु की नकल करने के लिए है, अंत में प्रत्येक प्रतिलिपि और अक्षम के बाद सभी प्रतियों सफल रहे हैं पूर्ववत करने के लिए। ऐसा करने से, यदि विनाश नहीं हो सकता है, तो आप मजबूत गारंटी सुनिश्चित करते हैं।
अक्षम उदाहरण:
//strong guarantee
void copy_to_all(BIGobj const& a) {
first_.push_back(a);
auto undo_first_push = finally([first_&] { first_.pop_back(); });
second_.push_back(a);
auto undo_second_push = finally([second_&] { second_.pop_back(); });
third_.push_back(a);
//no necessary, put just to make easier to add containers in the future
auto undo_third_push = finally([third_&] { third_.pop_back(); });
undo_first_push.disable();
undo_second_push.disable();
undo_third_push.disable(); }
यदि आप C ++ 11 का उपयोग नहीं कर सकते हैं , तो आप अभी भी अंत में हो सकते हैं , लेकिन कोड थोड़ा अधिक लंबा हो जाता है। बस एक रचना को केवल एक कंस्ट्रक्टर और डिस्ट्रक्टर के साथ परिभाषित करें, कंस्ट्रक्टर जरूरत की किसी भी चीज का संदर्भ लेता है और डिस्ट्रक्टर आपकी जरूरत के कार्यों को करता है। यह मूल रूप से लैम्बडा करता है, मैन्युअल रूप से किया जाता है।
#include <iostream>
int main() {
int* a = new int;
struct Delete_a_t {
Delete_a_t(int* p) : p_(p) {}
~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
int* p_;
} delete_a(a);
std::cout << "doing something ...\n"; }
FinalAction
मूल रूप से लोकप्रिय ScopeGuard
मुहावरे के रूप में ही है , केवल एक अलग नाम के साथ।
स्टैक-आधारित वस्तुओं के साथ सफाई को आसान बनाने से परे, RAII भी उपयोगी है क्योंकि एक ही 'स्वचालित' सफाई तब होती है जब ऑब्जेक्ट किसी अन्य वर्ग का सदस्य होता है। जब मालिक वर्ग को नष्ट कर दिया जाता है, तो RAII वर्ग द्वारा प्रबंधित संसाधन साफ हो जाता है क्योंकि उस वर्ग के लिए dtor को परिणाम के रूप में कहा जाता है।
इसका मतलब यह है कि जब आप RAII निर्वाण तक पहुँचते हैं और क्लास में सभी सदस्य RAII (स्मार्ट पॉइंटर्स की तरह) का उपयोग करते हैं, तो आप मालिक वर्ग के लिए एक बहुत ही सरल (शायद डिफ़ॉल्ट रूप से) dtor से दूर हो सकते हैं क्योंकि इसे मैन्युअल रूप से प्रबंधित करने की आवश्यकता नहीं है सदस्य संसाधन जीवनकाल।
ऐसा क्यों है कि प्रबंधित भाषाएं संसाधनों को स्वचालित रूप से कचरे के संग्रहकर्ता द्वारा वैसे भी ख़त्म किए जाने के बावजूद आखिरकार ब्लॉक प्रदान करती हैं?
दरअसल, गारबेज कलेक्टरों पर आधारित भाषाओं को "अंत में" और अधिक की आवश्यकता होती है। एक कूड़ा उठाने वाला आपकी वस्तुओं को समय पर नष्ट नहीं करता है, इसलिए गैर-मेमोरी से संबंधित मुद्दों को सही ढंग से साफ करने के लिए इस पर भरोसा नहीं किया जा सकता है।
गतिशील रूप से आवंटित डेटा के संदर्भ में, कई तर्क देंगे कि आपको स्मार्ट-पॉइंटर्स का उपयोग करना चाहिए।
तथापि...
RAII ऑब्जेक्ट के उपयोगकर्ता से डिजाइनर तक अपवाद सुरक्षा की जिम्मेदारी ले जाता है
अफसोस की बात है कि यह अपनी गिरावट है। पुरानी सी प्रोग्रामिंग आदतें कठिन मर जाती हैं। जब आप C या बहुत C शैली में लिखी गई लाइब्रेरी का उपयोग कर रहे हों, तो RAII का उपयोग नहीं किया जाएगा। संपूर्ण एपीआई फ्रंट-एंड को फिर से लिखने की कमी, बस यही आपके साथ काम करना है। फिर "अंत" की कमी वास्तव में काटती है।
CleanupFailedException
। क्या RAII का उपयोग करके इस तरह के परिणाम को प्राप्त करने का कोई प्रशंसनीय तरीका है?
SomeObject.DoSomething()
विधि को कॉल करेगा और जानना चाहेगा कि क्या यह (1) सफल हुआ, (2) कोई साइड-इफेक्ट्स के साथ विफल रहा, (3) साइड-इफेक्ट्स के साथ असफल कॉल करने वाले से निपटने के लिए तैयार है , या (4) साइड-इफ़ेक्ट्स के साथ विफल हो गया, जिससे कॉलर सामना नहीं कर सकता। केवल फोन करने वाले को यह पता चल जाता है कि वह किन परिस्थितियों में सामना कर सकता है; कॉलर को क्या चाहिए, यह जानने का एक तरीका है कि स्थिति क्या है। यह बहुत बुरा है कि अपवाद के बारे में सबसे महत्वपूर्ण जानकारी की आपूर्ति के लिए कोई मानक तंत्र नहीं है।
C ++ 11 लैम्ब्डा फ़ंक्शन का उपयोग करके एक और "अंततः" ब्लॉक इम्यूलेशन
template <typename TCode, typename TFinallyCode>
inline void with_finally(const TCode &code, const TFinallyCode &finally_code)
{
try
{
code();
}
catch (...)
{
try
{
finally_code();
}
catch (...) // Maybe stupid check that finally_code mustn't throw.
{
std::terminate();
}
throw;
}
finally_code();
}
चलो उम्मीद करते हैं कि संकलक ऊपर दिए गए कोड का अनुकूलन करेगा।
अब हम इस तरह कोड लिख सकते हैं:
with_finally(
[&]()
{
try
{
// Doing some stuff that may throw an exception
}
catch (const exception1 &)
{
// Handling first class of exceptions
}
catch (const exception2 &)
{
// Handling another class of exceptions
}
// Some classes of exceptions can be still unhandled
},
[&]() // finally
{
// This code will be executed in all three cases:
// 1) exception was not thrown at all
// 2) exception was handled by one of the "catch" blocks above
// 3) exception was not handled by any of the "catch" block above
}
);
यदि आप चाहें तो इस मुहावरे को "अंतत:" मैक्रोज़ में लपेट सकते हैं:
// Please never throw exception below. It is needed to avoid a compilation error
// in the case when we use "begin_try ... finally" without any "catch" block.
class never_thrown_exception {};
#define begin_try with_finally([&](){ try
#define finally catch(never_thrown_exception){throw;} },[&]()
#define end_try ) // sorry for "pascalish" style :(
अब "अंततः" ब्लॉक C ++ 11 में उपलब्ध है:
begin_try
{
// A code that may throw
}
catch (const some_exception &)
{
// Handling some exceptions
}
finally
{
// A code that is always executed
}
end_try; // Sorry again for this ugly thing
व्यक्तिगत रूप से मुझे "अंततः" मुहावरे के "मैक्रो" संस्करण पसंद नहीं है और उस मामले में एक सिंटैक्स अधिक भारी होने के बावजूद शुद्ध "with_finally" फ़ंक्शन का उपयोग करना पसंद करेंगे।
आप ऊपर दिए गए कोड का परीक्षण यहां कर सकते हैं: http://coliru.stacked-crooked.com/a/1d88f64cb2783813
पुनश्च
यदि आपको अपने कोड में अंत में ब्लॉक की आवश्यकता है , तो स्कॉप्ड गार्ड या ON_FINALLY / ON_EXCEPTION मैक्रोज़ शायद आपकी आवश्यकताओं को बेहतर रूप से फिट करेंगे।
यहाँ ON_FINALLY / ON_EXCEPTION के उपयोग का संक्षिप्त उदाहरण है:
void function(std::vector<const char*> &vector)
{
int *arr1 = (int*)malloc(800*sizeof(int));
if (!arr1) { throw "cannot malloc arr1"; }
ON_FINALLY({ free(arr1); });
int *arr2 = (int*)malloc(900*sizeof(int));
if (!arr2) { throw "cannot malloc arr2"; }
ON_FINALLY({ free(arr2); });
vector.push_back("good");
ON_EXCEPTION({ vector.pop_back(); });
...
इस तरह के एक पुराने धागे को खोदने के लिए क्षमा करें, लेकिन निम्नलिखित तर्क में एक बड़ी त्रुटि है:
RAII ऑब्जेक्ट के उपयोगकर्ता से ऑब्जेक्ट के डिज़ाइनर (और कार्यान्वयनकर्ता) तक अपवाद सुरक्षा की जिम्मेदारी ले जाता है। मेरा तर्क है कि यह सही जगह है क्योंकि आपको केवल एक बार (डिजाइन / कार्यान्वयन में) अपवाद सुरक्षा को सही करने की आवश्यकता है। अंत में किसी वस्तु का उपयोग करने पर आपको हर बार अपवाद सुरक्षा सही प्राप्त करने की आवश्यकता होती है।
अधिक बार नहीं, आपको गतिशील रूप से आवंटित वस्तुओं, वस्तुओं की गतिशील संख्या आदि से निपटना होगा, कोशिश-ब्लॉक के भीतर, कुछ कोड कई ऑब्जेक्ट्स बना सकते हैं (कितने रनटाइम पर निर्धारित होते हैं) और उन्हें सूची में स्टोर करते हैं। अब, यह एक विदेशी परिदृश्य नहीं है, लेकिन बहुत आम है। इस स्थिति में, आप सामान लिखना चाहेंगे
void DoStuff(vector<string> input)
{
list<Foo*> myList;
try
{
for (int i = 0; i < input.size(); ++i)
{
Foo* tmp = new Foo(input[i]);
if (!tmp)
throw;
myList.push_back(tmp);
}
DoSomeStuff(myList);
}
finally
{
while (!myList.empty())
{
delete myList.back();
myList.pop_back();
}
}
}
बेशक सूची दायरे से बाहर जाने पर ही नष्ट हो जाएगी, लेकिन इससे आपके द्वारा बनाई गई अस्थायी वस्तुओं की सफाई नहीं होगी।
इसके बजाय, आपको बदसूरत मार्ग पर जाना होगा:
void DoStuff(vector<string> input)
{
list<Foo*> myList;
try
{
for (int i = 0; i < input.size(); ++i)
{
Foo* tmp = new Foo(input[i]);
if (!tmp)
throw;
myList.push_back(tmp);
}
DoSomeStuff(myList);
}
catch(...)
{
}
while (!myList.empty())
{
delete myList.back();
myList.pop_back();
}
}
इसके अलावा: ऐसा क्यों है कि प्रबंधित लानूज़, संसाधनों को स्वचालित रूप से कचरा संग्रहकर्ता द्वारा वैसे भी ख़त्म किए जाने के बावजूद आखिरकार ब्लॉक प्रदान करते हैं?
सुझाव: वहाँ अधिक है कि आप "मेमोरी" की तुलना में "अंत में" कर सकते हैं।
new
NULL को नहीं लौटाता, इसके बजाय एक अपवाद फेंकता है
std::shared_ptr
और std::unique_ptr
stdlib शामिल हैं।
FWIW, Microsoft Visual C ++ समर्थन का प्रयास करता है, आखिरकार और इसे ऐतिहासिक रूप से MFC ऐप में गंभीर अपवादों को पकड़ने के एक तरीके के रूप में उपयोग किया गया है जो अन्यथा दुर्घटना का परिणाम होगा। उदाहरण के लिए;
int CMyApp::Run()
{
__try
{
int i = CWinApp::Run();
m_Exitok = MAGIC_EXIT_NO;
return i;
}
__finally
{
if (m_Exitok != MAGIC_EXIT_NO)
FaultHandler();
}
}
मैंने अतीत में इसका उपयोग बाहर निकलने से पहले खुली फाइलों के बैकअप को बचाने जैसे कामों के लिए किया है। कुछ JIT डिबगिंग सेटिंग्स इस तंत्र को तोड़ देंगी।
जैसा कि अन्य उत्तरों में बताया गया है, C ++ समान finally
-कार्यक्षमता का समर्थन कर सकता है । मानक भाषा का हिस्सा होने के लिए संभवतः निकटतम इस कार्यक्षमता का कार्यान्वयन C ++ कोर दिशानिर्देशों में से एक है, Bjarne स्टॉस्ट्रुप और हर्ब सटर द्वारा संपादित C ++ का उपयोग करने के लिए सर्वोत्तम प्रथाओं का एक सेट। इसका कार्यान्वयन finally
दिशानिर्देश सहायता लाइब्रेरी (GSL) का एक हिस्सा है । दिशानिर्देशों के दौरान, finally
पुरानी शैली के इंटरफेस से निपटने के दौरान उपयोग की सिफारिश की जाती है, और इसकी स्वयं की एक दिशानिर्देश भी है, जिसका शीर्षक है क्लीनअप को व्यक्त करने के लिए एक अंतिम_ उपयोग वस्तु यदि कोई उपयुक्त संसाधन संभाल उपलब्ध नहीं है ।
इसलिए, न केवल सी ++ का समर्थन करता है finally
, वास्तव में इसे बहुत सारे सामान्य उपयोग के मामलों में उपयोग करने की सिफारिश की जाती है।
जीएसएल कार्यान्वयन का एक उदाहरण उपयोग इस तरह दिखेगा:
#include <gsl/gsl_util.h>
void example()
{
int handle = get_some_resource();
auto handle_clean = gsl::finally([&handle] { clean_that_resource(handle); });
// Do a lot of stuff, return early and throw exceptions.
// clean_that_resource will always get called.
}
GSL कार्यान्वयन और उपयोग पाओलो.बोलज़ोनी के उत्तर के समान है । एक अंतर यह है कि कॉल द्वारा बनाई गई वस्तु की gsl::finally()
कमी है disable()
। यदि आपको उस कार्यक्षमता की आवश्यकता है (कहते हैं, एक बार इकट्ठा होने के बाद संसाधन को वापस करने के लिए और कोई अपवाद होने के लिए बाध्य नहीं है), तो आप पाओलो के कार्यान्वयन को पसंद कर सकते हैं। अन्यथा, जीएसएल का उपयोग करना मानकीकृत सुविधाओं का उपयोग करने के करीब है जितना आपको मिलेगा।
वास्तव में नहीं, लेकिन आप उन्हें कुछ विस्तार के लिए अनुकरण कर सकते हैं, उदाहरण के लिए:
int * array = new int[10000000];
try {
// Some code that can throw exceptions
// ...
throw std::exception();
// ...
} catch (...) {
// The finally-block (if an exception is thrown)
delete[] array;
// re-throw the exception.
throw;
}
// The finally-block (if no exception was thrown)
delete[] array;
ध्यान दें कि मूल अपवाद को फिर से फेंकने से पहले अंततः-ब्लॉक खुद एक अपवाद फेंक सकता है, जिससे मूल अपवाद को खारिज कर दिया जा सकता है। यह जावा अंत में ब्लॉक के समान ही व्यवहार है। इसके अलावा, आप return
कोशिश और ब्लॉक को पकड़ने के अंदर उपयोग नहीं कर सकते ।
std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e);
finally
ब्लॉक के अंदर से अपवाद फेंकने की अनुमति देनी चाहिए ।
मैं एक के साथ आया था finally
मैक्रो इस्तेमाल किया जा सकता है कि लगभग की तरह ¹ finally
जावा में कीवर्ड; यह std::exception_ptr
और दोस्तों, लैम्ब्डा कार्यों और का उपयोग करता है std::promise
, इसलिए इसकी आवश्यकता होती है C++11
या ऊपर; यह यौगिक कथन अभिव्यक्ति का उपयोग भी करता है GCC एक्सटेंशन , जो क्लैंग द्वारा समर्थित है।
चेतावनी : इस उत्तर के एक पुराने संस्करण ने कई और सीमाओं के साथ अवधारणा के एक अलग कार्यान्वयन का उपयोग किया।
सबसे पहले, एक सहायक वर्ग को परिभाषित करते हैं।
#include <future>
template <typename Fun>
class FinallyHelper {
template <typename T> struct TypeWrapper {};
using Return = typename std::result_of<Fun()>::type;
public:
FinallyHelper(Fun body) {
try {
execute(TypeWrapper<Return>(), body);
}
catch(...) {
m_promise.set_exception(std::current_exception());
}
}
Return get() {
return m_promise.get_future().get();
}
private:
template <typename T>
void execute(T, Fun body) {
m_promise.set_value(body());
}
void execute(TypeWrapper<void>, Fun body) {
body();
}
std::promise<Return> m_promise;
};
template <typename Fun>
FinallyHelper<Fun> make_finally_helper(Fun body) {
return FinallyHelper<Fun>(body);
}
फिर वास्तविक मैक्रो है।
#define try_with_finally for(auto __finally_helper = make_finally_helper([&] { try
#define finally }); \
true; \
({return __finally_helper.get();})) \
/***/
इसका उपयोग इस तरह किया जा सकता है:
void test() {
try_with_finally {
raise_exception();
}
catch(const my_exception1&) {
/*...*/
}
catch(const my_exception2&) {
/*...*/
}
finally {
clean_it_all_up();
}
}
के उपयोग std::promise
बनाता है यह बहुत आसान लागू करने के लिए, लेकिन यह शायद भी काफी अनावश्यक भूमि के ऊपर का एक सा है जहाँ से आप केवल जरूरत कार्यक्षमताओं reimplementing से बचा जा सकता है का परिचय std::promise
।
¹ चेतावनी: वहाँ कुछ चीजें हैं जो काफी के जावा संस्करण की तरह काम नहीं करते हैं finally
। मेरे सर के ऊपर से चला गया:
break
के भीतर से बयान try
और catch()
, के ब्लॉक क्योंकि वे एक लैम्ब्डा समारोह के भीतर रहते हैं;catch()
बाद कम से कम एक ब्लॉक होना चाहिए try
: यह एक सी ++ आवश्यकता है;try
और catch()'s
ब्लॉक के भीतर कोई रिटर्न नहीं है , तो संकलन विफल हो जाएगा क्योंकि finally
मैक्रो कोड में विस्तारित हो जाएगा जो वापस लौटना चाहेगा void
। यह, एक प्रकार का मैक्रो होने के द्वारा , एक शून्य संस्करण हो सकता है finally_noreturn
।सब सब में, मुझे नहीं पता कि क्या मैं कभी भी इस सामान का उपयोग करूँगा, लेकिन इसके साथ खेलने में मज़ा आया। :)
catch(xxx) {}
की शुरुआत में एक असंभव ब्लॉक में डालकर कैविएट 2 से छुटकारा पा सकते हैं finally
, जहां कम से कम एक कैच ब्लॉक होने के प्रयोजनों के लिए xxx एक फर्जी प्रकार है।
catch(...)
होगा, है ना?
xxx
एक निजी नाम स्थान में एक अस्पष्ट प्रकार बनाते हैं जिसका उपयोग कभी नहीं किया जाएगा।
मेरे पास एक उपयोग का मामला है जहां मुझे लगता है कि सी ++ 11 भाषा का एक बिल्कुल स्वीकार्य हिस्सा finally
होना चाहिए , क्योंकि मुझे लगता है कि प्रवाह के दृष्टिकोण से पढ़ना आसान है। मेरे उपयोग का मामला एक उपभोक्ता / निर्माता श्रृंखला है, जहां एक प्रहरीnullptr
सभी थ्रेड्स को बंद करने के लिए रन के अंत में भेजा जाता है।
यदि C ++ ने इसका समर्थन किया है, तो आप चाहेंगे कि आपका कोड इस तरह दिखाई दे:
extern Queue downstream, upstream;
int Example()
{
try
{
while(!ExitRequested())
{
X* x = upstream.pop();
if (!x) break;
x->doSomething();
downstream.push(x);
}
}
finally {
downstream.push(nullptr);
}
}
मुझे लगता है कि यह अधिक तार्किक है कि लूप की शुरुआत में अपनी अंतिम घोषणा को लागू करना, क्योंकि यह लूप के बाहर निकलने के बाद होता है ... लेकिन यह इच्छाधारी सोच है क्योंकि हम इसे सी ++ में नहीं कर सकते। ध्यान दें कि कतार downstream
दूसरे धागे से जुड़ी है, इसलिए आप प्रहरी push(nullptr)
में नहीं डाल सकते downstream
क्योंकि यह इस बिंदु पर नष्ट नहीं हो सकता ... इसे तब तक जीवित रहने की आवश्यकता है जब तक कि दूसरा धागा प्राप्त न हो जाए nullptr
।
तो यहाँ है कि लैम्ब्डा के साथ RAII वर्ग का उपयोग कैसे किया जाए:
class Finally
{
public:
Finally(std::function<void(void)> callback) : callback_(callback)
{
}
~Finally()
{
callback_();
}
std::function<void(void)> callback_;
};
और यहां बताया गया है कि आप इसका उपयोग कैसे करते हैं:
extern Queue downstream, upstream;
int Example()
{
Finally atEnd([](){
downstream.push(nullptr);
});
while(!ExitRequested())
{
X* x = upstream.pop();
if (!x) break;
x->doSomething();
downstream.push(x);
}
}
जैसा कि कई लोगों ने कहा है, समाधान अंत में ब्लॉक से बचने के लिए C ++ 11 सुविधाओं का उपयोग करना है। सुविधाओं में से एक है unique_ptr
।
यहाँ Mephane का उत्तर RAII पैटर्न का उपयोग करके लिखा गया है।
#include <vector>
#include <memory>
#include <list>
using namespace std;
class Foo
{
...
};
void DoStuff(vector<string> input)
{
list<unique_ptr<Foo> > myList;
for (int i = 0; i < input.size(); ++i)
{
myList.push_back(unique_ptr<Foo>(new Foo(input[i])));
}
DoSomeStuff(myList);
}
C ++ मानक लाइब्रेरी कंटेनरों के साथ unique_ptr का उपयोग करने के लिए कुछ और परिचय यहां दिए गए हैं
मैं एक विकल्प प्रदान करना चाहूंगा।
यदि आप चाहते हैं कि अंततः ब्लॉक को हमेशा कहा जाता है, तो इसे अंतिम कैच ब्लॉक के बाद डालें (जो कि संभवत: catch( ... )
अपवाद नहीं पकड़ा जाना चाहिए )
try{
// something that might throw exception
} catch( ... ){
// what to do with uknown exception
}
//final code to be called always,
//don't forget that it might throw some exception too
doSomeCleanUp();
यदि आप अंत में एक अंतिम चीज के रूप में ब्लॉक करना चाहते हैं, जब कोई अपवाद फेंका जाता है, तो आप बूलियन स्थानीय चर का उपयोग कर सकते हैं - चलाने से पहले आप इसे गलत पर सेट करें और कोशिश ब्लॉक के बहुत अंत में सच्चा असाइनमेंट डालें, फिर चर के लिए ब्लॉक चेक को पकड़ने के बाद मूल्य:
bool generalAppState = false;
try{
// something that might throw exception
//the very end of try block:
generalAppState = true;
} catch( ... ){
// what to do with uknown exception
}
//final code to be called only when exception was thrown,
//don't forget that it might throw some exception too
if( !generalAppState ){
doSomeCleanUpOfDirtyEnd();
}
//final code to be called only when no exception is thrown
//don't forget that it might throw some exception too
else{
cleanEnd();
}
मुझे यह भी लगता है कि RIIA अपवाद से निपटने और आखिरकार होने के लिए पूरी तरह से उपयोगी प्रतिस्थापन नहीं है। BTW, मुझे भी लगता है कि RIIA एक बुरा नाम है। मैं इस प्रकार की कक्षाओं को 'जनवादी' कहता हूं और उनका उपयोग बहुत करता हूं। समय का 95% वे न तो संसाधनों को न तो प्रारंभिक कर रहे हैं और न ही प्राप्त कर रहे हैं, वे एक स्कोप के आधार पर कुछ परिवर्तन लागू कर रहे हैं, या कुछ पहले से ही सेट अप ले रहे हैं और सुनिश्चित करें कि यह नष्ट हो गया है। यह आधिकारिक प्रतिरूप नाम है, जो इंटरनेट का मोहताज है कि मेरा नाम भी बेहतर हो सकता है, यह सुझाव देने के लिए मुझे गाली दी जाती है।
मुझे लगता है कि यह उचित नहीं है कि चीजों के कुछ तदर्थ सूची के हर जटिल सेटअप के लिए इसे लिखने के लिए एक वर्ग होना चाहिए ताकि जटिलताओं से बचने के लिए इसे साफ करने के लिए सभी को पकड़ने के लिए जरूरत पड़ने पर सामना करना पड़े। अपवाद प्रकार यदि प्रक्रिया में कुछ गलत हो जाता है। यह बहुत से तदर्थ वर्गों को जन्म देगा, जो कि अन्यथा आवश्यक नहीं होगा।
हाँ यह उन वर्गों के लिए ठीक है जो किसी विशेष संसाधन, या सामान्य लोगों को प्रबंधित करने के लिए डिज़ाइन किए गए हैं जो समान संसाधनों के एक सेट को संभालने के लिए डिज़ाइन किए गए हैं। लेकिन, भले ही शामिल सभी चीजों में इस तरह के रैपर हों, हो सकता है कि सफाई का समन्वय न केवल विनाशकारियों के रिवर्स ऑर्डर आह्वान में एक सरल हो।
मुझे लगता है कि यह अंत में सी ++ के लिए सही अर्थ है। मेरा मतलब है, भगवान, इतने सारे बिट्स और बोब्स पिछले दशकों में इस पर चिपके हुए हैं कि ऐसा लगता है कि अजीब लोग अचानक किसी चीज पर रूढ़िवादी हो जाएंगे, जो कि काफी उपयोगी हो सकता है और संभवतः कुछ अन्य चीजों के रूप में जटिल के पास कुछ भी नहीं है। जोड़ा गया (हालांकि यह मेरी ओर से सिर्फ एक अनुमान है।)
try
{
...
goto finally;
}
catch(...)
{
...
goto finally;
}
finally:
{
...
}
finally
ऐसा नहीं करता है।