সি ++ 'অবশেষে' অবরুদ্ধ করে? (এবং এই 'আরআইআইআই' আমি কী শুনছি?)


272

সি ++ ' অবশেষে ' অবরুদ্ধ করে?

আরআইআইআইপ্রতিবাদ কি ?

C ++ এর RAII আইডিয়াম এবং সি # 's' বিবৃতি ব্যবহারের মধ্যে পার্থক্য কী ?


1
: এছাড়াও উত্তর দেখুন stackoverflow.com/questions/7779652/...
18446744073709551615

উত্তর:


273

না, সি ++ 'অবশেষে' ব্লকগুলিকে সমর্থন করে না। "রিসোর্স অধিগ্রহণ হল সূচনা" - A: কারণ সি ++ পরিবর্তে RAII সমর্থন করে দরিদ্র নাম সত্যিই একটি দরকারী ধারণা জন্য।

ধারণাটি হ'ল কোনও বস্তুর ডেস্ট্রাক্টর সম্পদ মুক্ত করার জন্য দায়বদ্ধ। যখন অবজেক্টটির স্বয়ংক্রিয় স্টোরেজ সময়কাল থাকে, তখন অবজেক্টের ডেস্ট্রাক্টরকে ডাকা হবে যখন এটি তৈরি করা ব্লকটি বেরিয়ে আসে - এমনকি যখন সেই ব্লকটি ব্যতিক্রমের উপস্থিতিতে উপস্থিত থাকে। বিষয়টির ব্যাখ্যা এখানে বর্জন স্ট্রস্ট্রুপের দেওয়া আছে

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 অন্যান্য শ্রেণীর সদস্য হিসাবেও অবজেক্টগুলি ব্যবহার করা সহজ করে। মালিকানাধীন শ্রেণি 'ধ্বংস হয়ে গেলে, আরআইআইআই ক্লাস দ্বারা পরিচালিত সংস্থানটি প্রকাশিত হয় কারণ রায়-পরিচালিত শ্রেণীর ধ্বংসকারীকে ফলাফল হিসাবে ডেকে আনে। এর অর্থ হ'ল আপনি যখন সংস্থানগুলি পরিচালনা করে এমন কোনও শ্রেণীর সমস্ত সদস্যের জন্য আরআইআই ব্যবহার করেন, আপনি খুব সাধারণ, সম্ভবত এমনকি ডিফল্ট, মালিক শ্রেণীর জন্য ডেস্ট্রাক্টর ব্যবহার করে চলে যেতে পারেন কারণ এর সদস্যপদটির আধ্যাত্মিক জীবনকাল ম্যানুয়ালি ম্যানেজ করার দরকার নেই doesn't । (এটি নির্দেশ করার জন্য মাইক বিকে ধন্যবাদ ))

সি # বা ভিবি.এনইটি সহ ফ্যামিলিয়ারগুলির জন্য, আপনি সনাক্ত করতে পারেন যে RAII আইডিজিপোজযোগ্য এবং 'ব্যবহার' বিবৃতি ব্যবহার করে নেট নেট নির্বাহী ধ্বংসের সমান । প্রকৃতপক্ষে, দুটি পদ্ধতি খুব একই রকম। মূল পার্থক্য হ'ল RAII স্মৃতি সহ যেকোন ধরণের সংস্থানকে নির্বিচারে প্রকাশ করবে। .NET (এমনকি। নেট ভাষা সি ++ / সিএলআই) তে আইডিস্পোজেবল বাস্তবায়ন করার সময়, স্মৃতি ছাড়া স্মৃতি বাদ দিয়ে সংস্থানগুলি ছেড়ে দেওয়া হবে। .NET- এ, মেমরিটি নির্বিচারে প্রকাশিত হয় না; মেমরি কেবল আবর্জনা সংগ্রহের চক্রের সময় প্রকাশিত হয়।

 

† কিছু লোক বিশ্বাস করে যে "ধ্বংস হ'ল রিসোর্স রিজিকুইজমেন্ট" এটি রাই আইআইওমের আরও সঠিক নাম।


18
"ধ্বংস হ'ল রিসোর্স রিলিনজিশমেন্ট" - ডিআইআরআর ... না, আমার পক্ষে কাজ করে না। = পি
এরিক ফোর্বস

14
RAII আটকে আছে - সত্যিই এটির কোনও পরিবর্তন নেই। এটি করার চেষ্টা করা বোকামি হবে। তবে আপনাকে স্বীকার করতে হবে যদিও "রিসোর্স অ্যাকুইজিশন ইজ ইনিশিয়ালাইজেশন" এখনও বেশ খারাপ নাম।
কেভিন

162
এসবিআরএম == স্কোপ বাউন্ড রিসোর্স ম্যানেজমেন্ট
জোহানেস স্কাউব -

10
ইঞ্জিনিয়ার দক্ষতার সাথে যে কেউ কেবল সফ্টওয়্যারকেই সাধারণত করেনি, উন্নত কৌশলগুলি একা রাখুন, এইরকম ভয়াবহ সংক্ষিপ্ত আকারের জন্য উপযুক্ত অজুহাত দিতে পারে না।
হার্ডরিভ

54
আপনার যখন পরিষ্কার করার মতো কিছু থাকে যা কোনও সি ++ অবজেক্টের জীবদ্দশায় মেলে না তখন এটি আপনাকে আটকে দেয়। আমি অনুমান করি আপনি লাইফটাইম সমান সি ++ ক্লাস লাইফটাইম বা অন্যথায় এটি কুৎসিত (LECCLEOEIGU?) দিয়ে শেষ করেছেন।
ওয়ারেন পি

79

সি ++ এ শেষ অবধি নেই RAII এর কারণে প্রয়োজন ।

RAII অবজেক্টের ব্যবহারকারী থেকে অবজেক্ট সুরক্ষার দায়িত্বটিকে অবজেক্টটির ডিজাইনার (এবং প্রয়োগকারী) এর দিকে নিয়ে যায়। আমি তর্ক করব এটি সঠিক জায়গা কারণ আপনার কেবলমাত্র ব্যতিক্রমী সুরক্ষা একবারে নেওয়া দরকার (নকশা / প্রয়োগের ক্ষেত্রে)। অবশেষে ব্যবহার করে আপনি প্রতিবার কোনও জিনিস ব্যবহার করার সময় ব্যতিক্রমী সুরক্ষাটি সঠিকভাবে নেওয়া দরকার correct

আইএমও কোডটি আরও সুন্দর দেখাচ্ছে (নীচে দেখুন))

উদাহরণ:

একটি ডাটাবেস অবজেক্ট। ডিবি সংযোগটি ব্যবহার করা হয়েছে তা নিশ্চিত করার জন্য এটি অবশ্যই খোলা এবং বন্ধ করতে হবে। 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 এর ব্যবহার একটি ডিবি অবজেক্ট সঠিকভাবে ব্যবহার করা খুব সহজ করে। আমরা যেভাবে চেষ্টা করি না কেন এবং অপব্যবহার করি না কেন ডিবি অবজেক্টটি ডিস্ট্রাক্টর ব্যবহার করে সঠিকভাবে নিজেকে বন্ধ করে দেবে।

শেষ পর্যন্ত জাভা লাইক

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


16
// Make sure not to throw exception if one is already propagating.সি ++ ডিসস্ট্রাক্টরদের পক্ষে এই কারণেই ব্যতিক্রম না ছড়ানো উচিত।
Cemafor

10
@ চেমফোর: সি ++ ডিস্ট্রাক্টরের বাইরে ব্যতিক্রম না ছড়ানোর কারণ জাভা থেকে আলাদা। জাভাতে এটি কাজ করবে (আপনি কেবল আসল ব্যতিক্রমটি শিথিল করেছেন)। সি ++ এ এটি সত্যিই খারাপ। তবে সি ++ এর মূল বিষয়টি হ'ল তিনি যখন ডেস্ট্রাক্টর লেখেন তখন আপনাকে কেবল এটি একবার করতে হবে (শ্রেণীর ডিজাইনার দ্বারা)। জাভাতে আপনাকে এটি ব্যবহারের স্থানে করতে হবে। সুতরাং ক্লাসের ব্যবহারকারীর দায়িত্ব একই সময় একই বয়লার প্লেট লিখতে হবে।
মার্টিন ইয়র্ক

1
যদি এটি "প্রয়োজন" হওয়ার বিষয়টি হয় তবে আপনার আরআইআইআইয়ের দরকার নেই। এর থেকে মুক্তি পাওয়া যাক! :-) জোকস একদিকে রেখে, RAII অনেক ক্ষেত্রে ভাল fine আরআইআই যা আরও জটিল করে তোলে তা হ'ল আপনি যখন কিছু কোড কার্যকর করতে চান (সংস্থান সম্পর্কিত নয়) এমনকি উপরের কোডটি দ্রুত ফিরে আসে এমন কি। এর জন্য হয় আপনি গোটোস ব্যবহার করুন বা এটিকে দুটি পদ্ধতিতে পৃথক করুন।
ত্রিনিদাদ

1
@ ত্রিনিদাদ: আপনি যা ভাবেন এটি সহজ নয় (আপনার সমস্ত পরামর্শ সম্ভবত সবচেয়ে খারাপ বিকল্পগুলি বেছে নেবে)। যে কারণে কোনও প্রশ্নের মন্তব্যগুলির চেয়ে এটি অন্বেষণের জন্য ভাল জায়গা হতে পারে।
মার্টিন ইয়র্ক

1
"RAII এর কারণে প্রয়োজন হয় না" সমালোচনা করা: এমন অনেকগুলি কেস রয়েছে যেখানে অ্যাড-হক রাই যুক্ত করা খুব বেশি বয়লারপ্লেট কোড হবে এবং শেষ পর্যন্ত চেষ্টা করা অত্যন্ত উপযুক্ত হবে।
ceztko

63

RAII সাধারণত ভাল, তবে আপনি সহজেই সি ++ এ শেষ অবধি শব্দার্থক থাকতে পারেন । একটি ক্ষুদ্র পরিমাণের কোড ব্যবহার করা।

এছাড়াও, সি ++ মূল নির্দেশিকা শেষ পর্যন্ত দেয়।

এখানে জিএসএল মাইক্রোসফ্ট বাস্তবায়নের একটি লিঙ্ক এবং মার্টিন মোইন প্রয়োগের একটি লিঙ্ক রয়েছে

বাজর্ন স্ট্রাস্ট্রপ একাধিকবার বলেছিল যে জিএসএলে যা আছে তার সবশেষে স্ট্যান্ডার্ডে যাওয়ার অর্থ। সুতরাং অবশেষে এটি ব্যবহারের ভবিষ্যত-প্রমাণ উপায় হওয়া উচিত ।

আপনি চাইলে সহজেই নিজেকে বাস্তবায়ন করতে পারেন, পড়া চালিয়ে যান।

সি ++ এ 11 আরআইআই এবং ল্যাম্বডাস শেষ পর্যন্ত একটি সাধারণ তৈরি করতে দেয়:

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!

ব্যক্তিগতভাবে আমি একটি সি ++ প্রোগ্রামে পসেক্স ফাইল ডেস্ক্রিপ্টরটি বন্ধ করার জন্য এটি কয়েকবার ব্যবহার করেছি।

একটি রিয়েল ক্লাস থাকা যা রিসোর্সগুলি পরিচালনা করে এবং তাই কোনও প্রকার ফাঁস এড়িয়ে যায় সাধারণত ভাল তবে এটি শেষ পর্যন্ত এমন ক্ষেত্রে কার্যকর যেখানে শ্রেণি তৈরি করা ওভারকিলের মতো শোনাচ্ছে।

এছাড়াও, আমি অন্য ভাষার চেয়ে এটি শেষ পর্যন্ত পছন্দ করি কারণ প্রাকৃতিকভাবে ব্যবহৃত হলে আপনি প্রারম্ভিক কোডটির নিকটবর্তী সমাপ্তি কোডটি লিখেন (আমার উদাহরণে নতুন এবং মুছুন ) এবং ধ্বংসটি সি ++ এর মতো যথারীতি লিফোর ক্রমে নির্মাণ অনুসরণ করে। একমাত্র ক্ষতি হ'ল আপনি একটি অটো ভেরিয়েবল পাবেন যা আপনি সত্যই ব্যবহার করেন না এবং ল্যাম্বদা সিনট্যাক্স এটিকে কিছুটা গোলমাল করে তোলে (আমার উদাহরণে চতুর্থ লাইনে কেবল অবশেষে শব্দটি এবং ডানদিকে block block-ব্লক অর্থবহ, বিশ্রামটি মূলত গোলমাল)।

আরেকটি উদাহরণ:

 [...]
 auto precision = std::cout.precision();
 auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
 std::cout << std::setprecision(3);

নিষ্ক্রিয় যদি সদস্য দরকারী পরিশেষে শুধুমাত্র ব্যর্থতার ক্ষেত্রে বলা হবে। উদাহরণস্বরূপ, আপনাকে তিনটি ভিন্ন ধারক মধ্যে একটি বিষয় অনুলিপি করতে হবে, প্রতিটি অনুলিপি পূর্বাবস্থায় ফেলার জন্য আপনি শেষ পর্যন্ত সেটআপ করতে পারেন এবং সমস্ত অনুলিপি সফল হওয়ার পরে অক্ষম করতে পারেন। এটি করা, যদি ধ্বংসটি ছুঁতে না পারে তবে আপনি দৃ strong় গ্যারান্টি নিশ্চিত করেন।

অক্ষম উদাহরণ:

//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(); }

আপনি যদি সি ++ 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"; }

একটি সম্ভাব্য সমস্যা থাকতে পারে: ফাংশনে 'অবশেষে (F f)' এ এটি FinalAction এর একটি অবজেক্ট ফিরিয়ে দেয়, তাই ডিকনস্ট্রাক্টরকে শেষ পর্যন্ত ফাংশন ফেরার আগে ডেকে আনা যেতে পারে। হতে পারে আমাদের
স্ট্যাম্প

নোট যে FinalActionমূলত জনপ্রিয় ScopeGuardআইডিয়াম হিসাবে একই , শুধুমাত্র একটি ভিন্ন নাম দিয়ে।
Anderas

1
এই অপটিমাইজেশন নিরাপদ?
নুলানো

2
@ পাওলো.বোলজনি শীঘ্রই উত্তর না দেওয়ার জন্য দুঃখিত, আমি আপনার মন্তব্যের জন্য একটি বিজ্ঞপ্তি পাইনি। আমি উদ্বিগ্ন ছিলাম অবশেষে অবরুদ্ধকরণ (যার মধ্যে আমি একটি ডিএলএল ফাংশন বলি) সুযোগ শেষ হওয়ার আগে ডাকা হবে (কারণ ভেরিয়েবলটি অব্যবহৃত) তবে আমার উদ্বেগগুলি সাফ করার জন্য এসও তে একটি প্রশ্ন পেয়েছি। আমি এটিতে লিঙ্ক করব, তবে দুর্ভাগ্যক্রমে, আমি এটি আর খুঁজে পাচ্ছি না।
নুলানো

1
অক্ষম () ফাংশনটি আপনার অন্যথায় পরিষ্কার নকশায় এক ধরনের ওয়ার্ট। যদি আপনি চান যে অবশেষে কেবল ব্যর্থতার ক্ষেত্রে ডাকা হয়, তবে কেন কেবল ক্যাচ স্টেটমেন্ট ব্যবহার করবেন না? এটা কি এর জন্য নয়?
ব্যবহারকারী 2445507

32

স্ট্যাক-ভিত্তিক অবজেক্টের সাহায্যে সাফাই সহজ করা ছাড়াও, RAII এছাড়াও দরকারী কারণ একই জিনিস 'স্বয়ংক্রিয়' ক্লিন আপ ঘটে যখন বস্তুটি অন্য শ্রেণীর সদস্য হয়। মালিকানাধীন শ্রেণিটি ধ্বংস হয়ে গেলে, আরএআইআই ক্লাস দ্বারা পরিচালিত সংস্থানটি পরিষ্কার হয়ে যায় কারণ class শ্রেণীর জন্য দোসর ফলাফল হিসাবে ডেকে আনে।

এর অর্থ হ'ল আপনি যখন আরএআইআই নির্বান পৌঁছে যান এবং ক্লাসে থাকা সমস্ত সদস্যরা আরআইআইআই ব্যবহার করে (স্মার্ট পয়েন্টারগুলির মতো), আপনি মালিক শ্রেণীর জন্য খুব সাধারণ (সম্ভবত এমনকি ডিফল্ট) ডর্টর নিয়েও চলে যেতে পারেন যেহেতু এটি নিজে নিজে পরিচালনা করার দরকার নেই need সদস্য সংস্থান জীবনকাল।


এটি একটি খুব ভাল পয়েন্ট। আপনাকে +1 অন্য কোনও মানুষ আপনাকে ভোট দেয়নি। আমি আশা করি আপনার মন্তব্যগুলি অন্তর্ভুক্ত করার জন্য আমি আমার পোস্টটি সম্পাদনা করেছি তাতে আপত্তি নেই। (আমি আপনাকে অবশ্যই কৃতিত্ব দিয়েছিলাম।) ধন্যবাদ! :)
কেভিন 16

30

কেন এমন কি কেন যে ম্যানেজ করা ভাষাগুলি যে কোনও উপায়ে আবর্জনা সংগ্রহকারী কর্তৃক স্বয়ংক্রিয়ভাবে স্বয়ংক্রিয়ভাবে অবনমিত হওয়া সত্ত্বেও অবশেষে ব্লক সরবরাহ করে?

আসলে, আবর্জনা সংগ্রহকারীদের উপর ভিত্তি করে ভাষাগুলির আরও "অবশেষে" প্রয়োজন need কোনও আবর্জনা সংগ্রহকারী সময় মতো আপনার জিনিসগুলি ধ্বংস করে না, তাই এটি অ-স্মৃতি সম্পর্কিত সমস্যাগুলি সঠিকভাবে পরিষ্কার করার উপর নির্ভর করা যায় না।

গতিশীলভাবে বরাদ্দকৃত ডেটার ক্ষেত্রে, অনেকে তর্ক করবে যে আপনার স্মার্ট-পয়েন্টার ব্যবহার করা উচিত।

যাহোক...

RAII অবজেক্ট সুরক্ষার দায়িত্বটি অবজেক্টের ব্যবহারকারীর কাছ থেকে ডিজাইনারের দিকে নিয়ে যায়

দুঃখের বিষয় এটি নিজস্ব পতন। পুরানো সি প্রোগ্রামিং অভ্যাস হার্ড মারা যায়। আপনি যখন সি বা খুব সি স্টাইলে লেখা একটি লাইব্রেরি ব্যবহার করছেন তখন RAII ব্যবহার করা হত না। পুরো এপিআই এর সম্মুখ-প্রান্তটি পুনরায় লেখার সংক্ষিপ্ততা, আপনার সাথে কাজ করতে হবে এটিই। তারপরে "অবশেষে" অভাব সত্যিই কামড় দেয়।


13
ঠিক ... RAII আদর্শ দৃষ্টিকোণ থেকে দুর্দান্ত মনে হচ্ছে। তবে আমাকে সর্বদা প্রচলিত সি এপিআই দিয়ে কাজ করতে হবে (যেমন উইন 32 এপিআই-এর সি-স্টাইল ফাংশন ...)। এমন একটি সংস্থান অর্জন করা খুব সাধারণ যা কোনও প্রকারের হ্যান্ডেল দেয়, যার পরে ক্লোজহ্যান্ডল (হ্যান্ডেল) পরিষ্কার করার জন্য কিছু ফাংশন প্রয়োজন requires চেষ্টা করে ... শেষ পর্যন্ত সম্ভাব্য ব্যতিক্রমগুলি মোকাবেলার একটি দুর্দান্ত উপায়। (ধন্যবাদ, এটি দেখতে কাস্টম মোছার সাথে ভাগ করা_পিটার এবং সি ++ 11 ল্যাম্বডাসকে কিছু RAII- ভিত্তিক ত্রাণ সরবরাহ করা উচিত যা আমি কেবল একটি জায়গায় ব্যবহার করি এমন কিছু এপিআই মোড়ানোর জন্য পুরো ক্লাসগুলি লেখার প্রয়োজন হয় না))
জেমস জনস্টন

7
@ জেমস জনস্টন, একটি র‌্যাপার ক্লাস লিখতে খুব সহজ যা কোনও ধরণের হ্যান্ডেল ধরে রাখে এবং আরএআইআই মেকানিক্স সরবরাহ করে। এটিএল উদাহরণস্বরূপ তাদের একটি গুচ্ছ সরবরাহ করে। দেখে মনে হচ্ছে আপনি এটিকে খুব বেশি সমস্যা হিসাবে বিবেচনা করেছেন তবে আমি একমত নন, এগুলি খুব ছোট এবং সহজেই লেখা সহজ।
মার্ক রান্সম

5
সহজ হ্যাঁ, ছোট না। আপনি যে লাইব্রেরির সাথে কাজ করছেন তার জটিলতার উপর আকার নির্ভরশীল।
ফিলিপ কুলিং

1
@ মার্কারান্সম: এমন কোনও প্রক্রিয়া আছে যার মাধ্যমে আরআইআই বুদ্ধিমান কিছু করতে পারে যদি ক্লিনআপের সময় অন্য কোনও ব্যতিক্রম বিচারাধীন অবস্থায় থাকে? চেষ্টা / শেষ পর্যন্ত সিস্টেমগুলিতে, এমন কিছু করা সম্ভব - বিশ্রী হলেও - এমন জিনিসগুলি সাজানো যাতে মুলতুবি ব্যতিক্রম এবং ক্লিনআপ চলাকালীন ব্যতিক্রম উভয়ই একটি নতুন জায়গায় সঞ্চিত হয় CleanupFailedException। আরআইআইআই ব্যবহার করে এই জাতীয় ফলাফল অর্জনের জন্য কি কোনও প্রশংসনীয় উপায় আছে?
সুপারক্যাট

3
@ কৌলিং: এমন অনেকগুলি ঘটনা রয়েছে যেখানে একটি প্রোগ্রাম কোনও SomeObject.DoSomething()পদ্ধতি কল করে এটি জানতে চায় যে এটি (1) সফল হয়েছে কিনা, (2) কোনও পার্শ্ব-প্রতিক্রিয়া ছাড়াই ব্যর্থ হয়েছে , (3) পার্শ্ব-প্রতিক্রিয়াতে ব্যর্থ কলার সাথে মোকাবিলা করার জন্য প্রস্তুত , বা (4) পার্শ্ব প্রতিক্রিয়া নিয়ে ব্যর্থ কলার সহ্য করতে পারে না। কেবলমাত্র আহ্বায়ক জানতে পারে যে এটি কোন পরিস্থিতিতে কী কী পরিস্থিতি মোকাবেলা করতে পারে এবং কী করতে পারে না; কলারের কী দরকার তা পরিস্থিতিটি কী তা জানার উপায়। এটি খুব খারাপ যে কোনও ব্যতিক্রম সম্পর্কে সর্বাধিক গুরুত্বপূর্ণ তথ্য সরবরাহের জন্য কোনও মানক ব্যবস্থা নেই।
সুপারক্যাট

9

সি ++ 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 :(

এখন "শেষ অবধি" ব্লকটি সি ++ 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

ব্যক্তিগতভাবে আমি "অবশেষে" আইডিয়ামের "ম্যাক্রো" সংস্করণটি পছন্দ করি না এবং সেই ক্ষেত্রে একটি সিনট্যাক্স আরও বিশাল হলেও সেক্ষেত্রে খাঁটি "উইথ_ফিনালি" ফাংশনটি ব্যবহার করতে পছন্দ করি।

আপনি উপরের কোডটি এখানে পরীক্ষা করতে পারেন: http://coliru.stacked-crooked.com/a/1d88f64cb27b3813

পুনশ্চ

আপনার কোডটিতে শেষ অবধি যদি আপনার প্রয়োজন হয় তবে স্কোপড গার্ড বা ওএন_ফিনাল / ওএনএক্সসিএসপিএশন ম্যাক্রোগুলি সম্ভবত আপনার প্রয়োজনের সাথে আরও ভাল ফিট করে।

এখানে ব্যবহারের সংক্ষিপ্ত উদাহরণ 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(); });

    ...

1
প্রথমটি আমার কাছে এই পৃষ্ঠায় উপস্থাপিত সমস্ত বিকল্পগুলির মধ্যে সর্বাধিক পঠনযোগ্য। +1
নিকোস

7

এই ধরণের পুরানো থ্রেডটি খননের জন্য দুঃখিত, তবে নিম্নলিখিত যুক্তিতে একটি বড় ত্রুটি রয়েছে:

RAII অবজেক্টের ব্যবহারকারী থেকে অবজেক্ট সুরক্ষার দায়িত্বটিকে অবজেক্টটির ডিজাইনার (এবং প্রয়োগকারী) এর দিকে নিয়ে যায়। আমি তর্ক করব এটি সঠিক জায়গা কারণ আপনার কেবলমাত্র ব্যতিক্রমী সুরক্ষা একবারে নেওয়া দরকার (নকশা / প্রয়োগের ক্ষেত্রে)। অবশেষে ব্যবহার করে আপনি প্রতিবার কোনও জিনিস ব্যবহার করার সময় ব্যতিক্রমী সুরক্ষাটি সঠিকভাবে নেওয়া দরকার correct

প্রায়শই না, আপনি গতিসম্পন্ন বরাদ্দ হওয়া অবজেক্টস, গতিশীল সংখ্যক অবজেক্টস ইত্যাদির সাথে মোকাবিলা করতে হবে ট্রাই-ব্লকের মধ্যে কিছু কোড অনেকগুলি অবজেক্ট তৈরি করতে পারে (রানটাইম সময়ে কতগুলি নির্ধারিত হয়) এবং তাদের তালিকায় একটি পয়েন্টার সঞ্চয় করে। এখন, এটি কোনও বহিরাগত দৃশ্য নয়, তবে খুব সাধারণ। এই ক্ষেত্রে, আপনি মত স্টাফ লিখতে চাই

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();
  }
}

এছাড়াও: কেন যে ম্যানেজ করা ল্যাঙ্গুয়েজগুলি যে কোনও উপায়ে আবর্জনা সংগ্রহকারী কর্তৃক স্বয়ংক্রিয়ভাবে স্বয়ংক্রিয়ভাবে অবনমিত হওয়া সত্ত্বেও অবশেষে ব্লক সরবরাহ করে?

ইঙ্গিত: কেবলমাত্র স্মৃতিশক্তি হ্রাসের চেয়ে "শেষ পর্যন্ত" আপনি আরও কিছু করতে পারেন।


17
পরিচালিত ভাষাগুলির অবশেষে অবরুদ্ধগুলি প্রয়োজন কারণ কেবলমাত্র এক ধরণের সংস্থান স্বয়ংক্রিয়ভাবে পরিচালিত হয়: মেমরি। RAII এর অর্থ হল যে সমস্ত সংস্থান একই পদ্ধতিতে পরিচালনা করা যায়, সুতরাং শেষ পর্যন্ত কোনও প্রয়োজন নেই। যদি আপনি প্রকৃতপক্ষে আপনার উদাহরণে RAII ব্যবহার করেন (নগ্নগুলির পরিবর্তে আপনার তালিকায় স্মার্ট পয়েন্টার ব্যবহার করে), কোডটি আপনার "শেষ অবধি"-নমুনার চেয়ে সহজ হবে। এমনকি যদি আপনি নতুনের রিটার্নের মানটি পরীক্ষা না করেন তবে আরও সহজ - এটি পরীক্ষা করা মোটামুটি অর্থহীন।
Myto

7
newনুল
ফেরেনি,

5
আপনি একটি গুরুত্বপূর্ণ প্রশ্ন উত্থাপন করেন, তবে এর 2 টি সম্ভাব্য উত্তর রয়েছে। মাইটোর দেওয়া একটি - সমস্ত গতিশীল বরাদ্দের জন্য স্মার্ট পয়েন্টার ব্যবহার করুন। অন্যটি হ'ল মানক পাত্রে ব্যবহার করা, যা সর্বদা ধ্বংসের পরে তাদের সামগ্রীগুলি ধ্বংস করে। যেভাবেই হোক, প্রতিটি বরাদ্দকৃত অবজেক্ট অবশেষে স্ট্যাটিক্যালি বরাদ্দকৃত বস্তুর মালিকানাধীন যা এটি স্বয়ংক্রিয়ভাবে ধ্বংসের পরে মুক্ত করে। এটি সত্যিকারের লজ্জাজনক যে প্লেইন পয়েন্টার এবং অ্যারের উচ্চ দৃশ্যমানতার কারণে প্রোগ্রামারদের পক্ষে এই আরও ভাল সমাধানগুলি আবিষ্কার করা শক্ত।
j_random_hacker

4
সি ++ 11 এটির উন্নতি করে std::shared_ptrএবং std::unique_ptrসরাসরি এবং stdlib এ অন্তর্ভুক্ত।
u0b34a0f6ae

16
আপনার উদাহরণটি এত ভয়াবহ দেখাচ্ছে কারণ আরআইআইআই ত্রুটিযুক্ত নয়, এটি কারণ আপনি এটি ব্যবহার করতে ব্যর্থ হয়েছেন। কাঁচা পয়েন্টারগুলি RAII নয়।
বেন ভয়েগট

6

এফডব্লিউআইডাব্লু, মাইক্রোসফ্ট ভিজ্যুয়াল সি ++ চেষ্টা করে দেখার চেষ্টা করে, শেষ পর্যন্ত এবং এটি এমএফসি অ্যাপ্লিকেশনগুলিতে গুরুতর ব্যতিক্রমগুলি ধরার পদ্ধতি হিসাবে historতিহাসিকভাবে ব্যবহৃত হয়েছে যা অন্যথায় ক্রাশ হতে পারে। উদাহরণ স্বরূপ;

int CMyApp::Run() 
{
    __try
    {
        int i = CWinApp::Run();
        m_Exitok = MAGIC_EXIT_NO;
        return i;
    }
    __finally
    {
        if (m_Exitok != MAGIC_EXIT_NO)
            FaultHandler();
    }
}

আমি অতীতে এইগুলি প্রস্থান করার আগে খোলা ফাইলগুলির ব্যাকআপ সংরক্ষণের মতো কাজ করতে ব্যবহার করেছি। কিছু নির্দিষ্ট জেআইটি ডিবাগিং সেটিংস যদিও এই ব্যবস্থাকে ভেঙে দেবে।


4
মনে রাখবেন যে এটি আসলে সি ++ ব্যতিক্রম নয়, তবে এসইএইচগুলি রয়েছে। আপনি এমএস সি ++ কোডে উভয়ই ব্যবহার করতে পারেন। এসইএইচ একটি ওএস ব্যতিক্রম হ্যান্ডলার যা উপায় ভিবি,। নেট ব্যতিক্রমগুলি কার্যকর করে।
gbjbaanb

এবং আপনি একটি 'গ্লোবাল' আন-ক্যাচড ব্যতিক্রম হ্যান্ডলার তৈরি করতে সেটউইনহ্যান্ডলএক্সেপশনহ্যান্ডলার ব্যবহার করতে পারেন - এসইএইচ ব্যতিক্রমগুলির জন্য।
gbjbaanb

3
এসইএইচ ভয়ঙ্কর এবং সি ++
ডিসস্ট্রাক্টরদের

6

অন্যান্য উত্তরে যেমন উল্লেখ করা হয়েছে, সি ++ finallyপছন্দসই কার্যকারিতা সমর্থন করে। সম্ভবত স্ট্যান্ডার্ড ভাষার অংশ হওয়ার সবচেয়ে কাছের এই কার্যকারিতাটির বাস্তবায়ন হ'ল সি ++ কোর গাইডলাইনস , ব্জন্ন স্টুস্ট্রপ এবং হার্ব সটার দ্বারা সম্পাদিত সি ++ ব্যবহারের জন্য সেরা অনুশীলনের একটি সেট। একটি বাস্তবায়নের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::finally()অভাবের দ্বারা নির্মিত বস্তুর disable()। আপনার যদি সেই কার্যকারিতাটির প্রয়োজন হয় (বলুন, একবার এটি সংস্থান করা হলে রিসোর্সটি ফিরিয়ে দিতে এবং কোনও ব্যতিক্রম ঘটতে বাধ্য নয়), আপনি পাওলোর বাস্তবায়ন পছন্দ করতে পারেন। অন্যথায়, জিএসএল ব্যবহার আপনার কাছে যেমন প্রমিত বৈশিষ্ট্যগুলি ব্যবহার করার তত কাছাকাছি।


3

আসলে নয়, তবে আপনি এগুলি কিছুটা প্রসারিত করতে পারেন, উদাহরণস্বরূপ:

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চেষ্টা এবং ব্লক ধরার ভিতরে ব্যবহার করতে পারবেন না ।


3
আমি আনন্দিত যে আপনি উল্লেখ করেছেন শেষ অবধি ব্লকটি ছুঁড়ে যেতে পারে; এটি এমন জিনিস যা বেশিরভাগ "RAII ব্যবহার করুন" উত্তরগুলি এড়ানো বলে মনে হচ্ছে। দু'বার অবশেষে ব্লকটি এড়াতে, আপনি যেমন কিছু করতে পারেনstd::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e);
শেঠোব্রায়েন

1
এটাই আমি জানতে চেয়েছিলাম! অন্য উত্তরের কোনও উত্তর কেন ব্যাখ্যা করল না যে একটি ধরা (...) + খালি নিক্ষেপ; প্রায় শেষ অবধি ব্লকের মতো কাজ করে? কখনও কখনও আপনার শুধু এটি প্রয়োজন।
ভিনগারিয়া

আমি আমার উত্তরে ( stackoverflow.com/a/38701485/566849 ) প্রদত্ত সমাধানটি finallyব্লকের ভিতরে থেকে ব্যতিক্রম ছোঁড়ার অনুমতি দেওয়া উচিত ।
ফ্যাবিও এ।

3

আমি নিয়ে এসেছেন finallyম্যাক্রো ব্যবহার করা যেতে পারে যে প্রায় মত ¹ finallyজাভা শব্দ; এটি std::exception_ptrএবং বন্ধুদের ব্যবহার করে , ল্যাম্বদা ফাংশন এবংstd::promise , সুতরাং এটির প্রয়োজন হয় C++11বা উপরে; এটি যৌগিক বিবৃতি এক্সপ্রেশন জিসিসি এক্সটেনশনটিও ব্যবহার করে, যা ঝনঝন দ্বারা সমর্থিত।

সতর্কতামূলক : এই উত্তরের পূর্ববর্তী সংস্করণটি আরও অনেক সীমাবদ্ধতার সাথে ধারণার একটি পৃথক বাস্তবায়ন ব্যবহার করেছে।

প্রথমে একটি সহায়ক শ্রেণীর সংজ্ঞা দেওয়া যাক।

#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 এটি বাস্তবায়ন করা খুব সহজ করে তোলে তবে এটি সম্ভবত অপ্রয়োজনীয় ওভারহেডের একটি বিট প্রচলন করে যা কেবলমাত্র প্রয়োজনীয় ক্রিয়াকলাপগুলি পুনরায় প্রয়োগ করে এড়ানো যায় std::promise


¹ কভেট: এমন কয়েকটি জিনিস রয়েছে যা জাভা সংস্করণের মতো পুরোপুরি কাজ করে না finally। আমার মাথার উপরে:

  1. breakস্টেটমেন্টটির সাথে বাইরের লুপটি ভেদ করা সম্ভব নয়try এবং catch()এর ব্লকগুলির , যেহেতু তারা ল্যাম্বডা ফাংশনের মধ্যে থাকে;
  2. কমপক্ষে একটি থাকতে হবে catch() পরে ব্লকtry : এটি একটি সি ++ প্রয়োজন;
  3. যদি ফাংশনটির শূন্য ব্যতীত অন্য কোনও ফেরতের মান থাকে তবে tryএবং catch()'sব্লকগুলির মধ্যে কোনও প্রত্যাবর্তন না ঘটে তবে সংকলন ব্যর্থ হবে কারণ finallyম্যাক্রো কোডটিতে প্রসারিত হবে যা একটিটি ফিরে আসতে চাইবে void। এই হতে পারে, পথ ভ্রষ্ট করবে, একটি অকার্যকরfinally_noreturn ম্যাক্রোর প্রকারের দ্বারা এড ।

সব মিলিয়ে আমি জানি না যে আমি এই জিনিসগুলি নিজেই ব্যবহার করতাম কিনা তবে এটির সাথে খেলে মজা পাওয়া গেল। :)


হ্যাঁ, এটি কেবলমাত্র একটি দ্রুত হ্যাক ছিল, তবে প্রোগ্রামার যদি জানতে পারে যে তারা কী করছে তা তবে তা কার্যকর হতে পারে।
ফ্যাবিও এ।

@ মারকলাকাটা, আমি পোস্টটি আরও ভাল বাস্তবায়নের সাথে আপডেট করেছি যা ছোঁড়া ব্যতিক্রম এবং রিটার্ন সমর্থন করে।
ফ্যাবিও এ।

ভাল লাগছে। আপনি ম্যাক্রোর catch(xxx) {}শুরুতে কেবল একটি অসম্ভব অবরুদ্ধ ব্লকটি রেখে কেভেট 2 থেকে মুক্তি পেতে পারেন finally, যেখানে কমপক্ষে একটি ক্যাচ ব্লক থাকার উদ্দেশ্যে xxx একটি বোগাস প্রকার।
লাকাটা

@ মারকলাকাটা, আমি সেটার কথাও ভেবেছিলাম, কিন্তু এটি ব্যবহার করা অসম্ভব করে catch(...)দেবে, তাই না?
ফ্যাবিও এ।

আমি তাই মনে করি না. xxxএকটি প্রাইভেট নেমস্পেসে কেবল একটি অস্পষ্ট ধরণ তৈরি করুন যা কখনই ব্যবহার করা হবে না।
লাকাতা

2

আমার একটি ব্যবহারের কেস রয়েছে যেখানে আমি মনে করি যে এটি সি ++ 11 ভাষার একটি নিখুঁতভাবে গ্রহণযোগ্য অংশ finally হওয়া উচিত , কারণ আমি মনে করি যে প্রবাহের দৃষ্টিকোণ থেকে এটি পড়া সহজ। আমার ব্যবহারের ক্ষেত্রটি থ্রেডের একটি ভোক্তা / প্রযোজক চেইন, যেখানে nullptrসমস্ত থ্রেড বন্ধ করার জন্য রান শেষে প্রেরণেল প্রেরণ করা হয়।

যদি সি ++ এটি সমর্থন করে তবে আপনি চাইবেন যে আপনার কোডটি এমন দেখাচ্ছে:

    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

সুতরাং এখানে ল্যাম্বদা সহ একটি আরআইআই ক্লাস কীভাবে ব্যবহার করবেন তা এখানে:

    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);
        }
    }

হাই, আমি বিশ্বাস করি উপরোক্ত আমার উত্তর ( stackoverflow.com/a/38701485/566849 ) আপনার প্রয়োজনীয়তা সম্পূর্ণরূপে সন্তুষ্ট করে।
ফ্যাবিও এ 14

1

অনেক লোক যেমন বলেছে, সমাধানটি হ'ল সি ++ 11 বৈশিষ্ট্যগুলি অবশেষে ব্লকগুলি এড়ানোর জন্য ব্যবহার করা। এর অন্যতম বৈশিষ্ট্য unique_ptr

এখানে 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);
}

সি ++ স্ট্যান্ডার্ড লাইব্রেরি পাত্রে ব্যবহার করে ইউনিক_পিটার ব্যবহারের আরও কিছু ভূমিকা এখানে


0

আমি একটি বিকল্প প্রদান করতে চাই।

আপনি যদি অবশেষে অবরুদ্ধটিকে সর্বদা কল করার জন্য চান তবে এটি সর্বশেষ ক্যাচ ব্লকের পরে রাখুন (যা সম্ভবত 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();
}

এই কাজ করে না, কারণ একটি পরিশেষে ব্লক পুরো পয়েন্ট পরিষ্করণ এমনকি সম্পাদন করতে যখন কোড উচিত একটি ব্যতিক্রম কোড ব্লক ছেড়ে করার অনুমতি দেয়। বিবেচনা করুন: `চেষ্টা করুন stuff // স্টাফ সম্ভবত" বি "} ক্যাচ (এএন্ডএ) throw} অবশেষে throw // নিক্ষেপ করছে যদি সি ++ থাকে ... // স্টাফ অবশ্যই ঘটবে, এমনকি" বি "নিক্ষেপ করা হলেও। B // "বি" ফেলে দিলে কার্যকর করা হবে না। `আইএমএইচও, ব্যতিক্রমগুলির বিন্দুটি হ'ল ত্রুটি-পরিচালনা কোডটি হ্রাস করা , সুতরাং যেখানেই কোনও ছোঁড়া ঘটতে পারে সেখানে ব্লক ধরুন, প্রতিরোধমূলক is এই কারণেই RAII সহায়তা করে: উদারভাবে প্রয়োগ করা হলে ব্যতিক্রমগুলি শীর্ষ এবং নীচের স্তরগুলিতে সর্বাধিক গুরুত্বপূর্ণ।
অস্পষ্টভাবে

1
@ বার্লিইয়েলি আপনার পক্ষে মতামত পবিত্র নয়, আমি পয়েন্টটি পেয়েছি, তবে সি ++ এ তেমন কিছুই নয় তাই আপনাকে এটিকে আচরণের অনুকরণ করে এমন একটি শীর্ষ স্তর হিসাবে বিবেচনা করতে হবে।
jave.web

ডাউনলোড করুন = দয়া করে মন্তব্য :)
jave.web

0

আমি আরও মনে করি যে ব্যতিক্রম পরিচালনা এবং শেষ পর্যন্ত থাকার জন্য আরআইআইএ কোনও সম্পূর্ণ কার্যকর প্রতিস্থাপন নয়। বিটিডাব্লু, আমিও মনে করি আরআইআইএ চারপাশে একটি খারাপ নাম। আমি এই ধরণের ক্লাসগুলিকে 'দরজা' বলি এবং তাদের প্রচুর ব্যবহার করি। 95% সময় তারা আরম্ভ করছে না বা সংস্থান অর্জন করছে না, তারা কোনও স্কোপড ভিত্তিতে কিছু পরিবর্তন প্রয়োগ করছে, বা ইতিমধ্যে সেট আপ করা হয়েছে এবং নিশ্চিত করেছে যে এটি ধ্বংস হয়ে গেছে। এটি আমার সরকারী প্যাটার্ন নাম হ'ল ইন্টারনেট আচ্ছন্ন হওয়া এমনকি আমার নামটি আরও ভাল হতে পারে বলে প্রস্তাব দেওয়ার জন্য আমি দুর্ব্যবহার করি।

আমি কেবল এটি যুক্তিসঙ্গত বলে মনে করি না যে প্রতিটি জটিল সেটআপ জিনিসগুলির কয়েকটি সেটআপের জন্য একটি ক্লাস লিখিত থাকতে হবে যাতে এটি রোধ করার সময় জটিলতাগুলি এড়ানোর জন্য সমস্ত কিছু ব্যাকআপের মুখোমুখি করার সময় এটির মুখোমুখি হতে হয় catch প্রক্রিয়াটিতে কিছু ভুল হলে ব্যতিক্রম প্রকারগুলি। এটি প্রচুর অ্যাডহক শ্রেণিতে নিয়ে যায় যা অন্যথায় প্রয়োজনীয় হবে না।

হ্যাঁ এটি একটি নির্দিষ্ট সংস্থান পরিচালনার জন্য ডিজাইন করা ক্লাসগুলির জন্য, বা অনুরূপ সংস্থানগুলির একটি সেট পরিচালনা করার জন্য ডিজাইন করা জেনেরিকগুলির পক্ষে এটি ঠিক। তবে, জড়িত সমস্ত কিছুর মধ্যে এমন মোড়ক থাকলেও, পরিষ্কারের সমন্বয়টি কেবল ধ্বংসকারীদের বিপরীতে আদেশের জন্য সহজ হতে পারে না।

আমি মনে করি সি ++ এর অবশেষে থাকার জন্য এটি সঠিক ধারণা দেয়। আমি বলতে চাই, জিজ, এতগুলি বিট এবং ববগুলি গত দশকগুলিতে এটির উপর আঠালো হয়ে গেছে যে মনে হচ্ছে যে অবাক লোকেরা হঠাৎ করে এমন কিছু বিষয়ে রক্ষণশীল হয়ে উঠবে যা বেশ কার্যকর হতে পারে এবং সম্ভবত কিছু অন্যান্য জিনিসের মতো জটিল কিছুই ছিল না যোগ করা হয়েছে (যদিও এটি আমার পক্ষ থেকে কেবল অনুমান))


-2
try
{
  ...
  goto finally;
}
catch(...)
{
  ...
  goto finally;
}
finally:
{
  ...
}

35
বুদ্ধিমান আইডিয়াম, তবে এটি একরকম নয়। চেষ্টা ব্লকে ফিরে আসা বা ধরা আপনার 'শেষ অবধি:' কোডের মধ্য দিয়ে যাবে না।
এডওয়ার্ড কেএমইটিটি

10
এই ভুল উত্তরটি রাখা (0 রেটিং সহ) মূল্যবান, কারণ এডওয়ার্ড কেমেট একটি অত্যন্ত গুরুত্বপূর্ণ পার্থক্য নিয়ে আসে।
লাকাটা

12
এমনকি আরও বড় ত্রুটি (আইএমও): এই কোডটি সমস্ত ব্যতিক্রম খায়, যা finallyতা করে না।
বেন ভয়েগট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.