আরআইআই এবং সি ++ এর স্মার্ট পয়েন্টার


193

সি ++ এর সাথে অনুশীলনে, আরআইআই কী, স্মার্ট পয়েন্টারগুলি কী কী , কোনও প্রোগ্রামে এগুলি কীভাবে প্রয়োগ করা হয় এবং স্মার্ট পয়েন্টারগুলির সাহায্যে আরআইআইআই ব্যবহার করার সুবিধা কী কী?

উত্তর:


317

RAII এর একটি সাধারণ (এবং সম্ভবত অতিরিক্ত ব্যবহৃত) উদাহরণ ফাইল ক্লাস। RAII ব্যতীত কোডটি এমন কিছু দেখতে পারে:

File file("/path/to/file");
// Do stuff with file
file.close();

অন্য কথায়, আমাদের অবশ্যই নিশ্চিত করতে হবে যে ফাইলটি শেষ হয়ে গেলে আমরা এটি বন্ধ করে দেব। এটির দুটি ঘাটতি রয়েছে - প্রথমত, যেখানেই আমরা ফাইল ব্যবহার করি না কেন, আমাদের ফাইল :: বন্ধ () বলতে হবে - যদি আমরা এটি করতে ভুলে যাই তবে আমরা ফাইলটি আমাদের প্রয়োজনের চেয়ে বেশি ধরে রেখেছি। দ্বিতীয় সমস্যাটি কি আমরা যদি ফাইলটি বন্ধ করার আগে একটি ব্যতিক্রম ছুঁড়ে ফেলা হয়?

জাভা শেষ সমস্যাটি ব্যবহার করে দ্বিতীয় সমস্যাটি সমাধান করে:

try {
    File file = new File("/path/to/file");
    // Do stuff with file
} finally {
    file.close();
}

বা জাভা 7-এর পরে, একটি রিসোর্স-স্টেটমেন্ট-সহ বিবৃতি:

try (File file = new File("/path/to/file")) {
   // Do stuff with file
}

সি ++ উভয়ই আরআইআইআই ব্যবহার করে সমস্যার সমাধান করে - তা হ'ল ফাইলের ডেস্ট্রাক্টরে ফাইলটি বন্ধ করে দেওয়া। যতক্ষণ না সঠিক সময়ে ফাইল অবজেক্টটি ধ্বংস হয়ে যায় (যা এটি যাইহোক হওয়া উচিত) ফাইলটি বন্ধ করা আমাদের জন্য যত্ন নেওয়া হয়। সুতরাং, আমাদের কোডটি এখন এমন কিছু দেখাচ্ছে:

File file("/path/to/file");
// Do stuff with file
// No need to close it - destructor will do that for us

এটি জাভাতে করা যাবে না কারণ বস্তুটি কখন ধ্বংস হয়ে যাবে তার কোনও গ্যারান্টি নেই, সুতরাং ফাইলের মতো কোনও উত্স যখন মুক্ত হবে তখন আমরা গ্যারান্টি দিতে পারি না।

স্মার্ট পয়েন্টারগুলিতে - অনেক সময়, আমরা কেবল স্ট্যাকের উপর বস্তু তৈরি করি। উদাহরণস্বরূপ (এবং অন্য উত্তর থেকে একটি উদাহরণ চুরি):

void foo() {
    std::string str;
    // Do cool things to or using str
}

এটি সূক্ষ্মভাবে কাজ করে - তবে আমরা কী আবার ফিরে আসতে চাই? আমরা এটি লিখতে পারে:

std::string foo() {
    std::string str;
    // Do cool things to or using str
    return str;
}

তো, তাতে দোষ কী? ঠিক আছে, রিটার্ন টাইপটি স্ট্যান্ডিং :: স্ট্রিং - সুতরাং এর অর্থ আমরা মান দিয়ে ফিরছি। এর অর্থ হ'ল আমরা আরআরটি অনুলিপি করছি এবং প্রকৃতপক্ষে অনুলিপিটি ফিরিয়ে দিই। এটি ব্যয়বহুল হতে পারে এবং আমরা এটি অনুলিপি করার খরচ এড়াতে চাই। সুতরাং, আমরা রেফারেন্স বা পয়েন্টার দ্বারা ফিরে আসার ধারণা নিয়ে আসতে পারি।

std::string* foo() {
    std::string str;
    // Do cool things to or using str
    return &str;
}

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

তো, এর সমাধান কী? আমরা নতুন ব্যবহার করে স্তূপের উপর স্ট্র তৈরি করতে পারি - এইভাবে, যখন foo () সম্পন্ন হবে, str বিনষ্ট হবে না।

std::string* foo() {
    std::string* str = new std::string();
    // Do cool things to or using str
    return str;
}

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

এখানেই স্মার্ট পয়েন্টারগুলি আসে The নীচের উদাহরণটি শেয়ারড_পিটার ব্যবহার করে - আমি আপনাকে পরামর্শ দিচ্ছি যে আপনি আসলে কী ব্যবহার করতে চান তা জানতে বিভিন্ন ধরণের স্মার্ট পয়েন্টারগুলি দেখুন।

shared_ptr<std::string> foo() {
    shared_ptr<std::string> str = new std::string();
    // Do cool things to or using str
    return str;
}

এখন, ভাগ করা_পিটিআর স্ট্রিংয়ের উল্লেখের সংখ্যা গণনা করবে। এই ক্ষেত্রে

shared_ptr<std::string> str = foo();
shared_ptr<std::string> str2 = str;

এখন একই স্ট্রিংয়ের দুটি উল্লেখ রয়েছে। একবার স্ট্রের কোনও অবশিষ্ট রেফারেন্স না থাকলে এটি মুছে ফেলা হবে। এর মতো, আপনার নিজের আর এটি মুছে ফেলার বিষয়ে আর চিন্তা করতে হবে না।

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

সুতরাং, আসুন আমাদের ফাইল ক্লাস ব্যবহার করে একটি ভিন্ন উদাহরণ চেষ্টা করুন।

ধরা যাক আমরা লগ হিসাবে কোনও ফাইল ব্যবহার করতে চাই। এর অর্থ আমরা কেবলমাত্র অ্যাপেন্ড মোডে আমাদের ফাইলটি খুলতে চাই:

File file("/path/to/file", File::append);
// The exact semantics of this aren't really important,
// just that we've got a file to be used as a log

এখন, আসুন কয়েক ফাইলের জন্য আমাদের ফাইলটিকে লগ হিসাবে সেট করুন:

void setLog(const Foo & foo, const Bar & bar) {
    File file("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

দুর্ভাগ্যক্রমে, এই উদাহরণটি ভয়াবহভাবে শেষ হয় - এই পদ্ধতিটি শেষ হওয়ার সাথে সাথে ফাইলটি বন্ধ হয়ে যাবে, যার অর্থ foo এবং বারটির এখন একটি অবৈধ লগ ফাইল রয়েছে। আমরা স্তূপে ফাইল তৈরি করতে পারি এবং foo এবং বার উভয়কেই ফাইলের জন্য একটি পয়েন্টারটি দিতে পারি:

void setLog(const Foo & foo, const Bar & bar) {
    File* file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

তবে তারপরে ফাইল মোছার দায় কে? যদি না হয় ফাইলটি মুছে ফেলা হয় তবে আমাদের মেমরি এবং রিসোর্স লিক উভয়ই রয়েছে। Foo বা বার ফাইলটি আগে শেষ করবে কিনা তা আমরা জানি না, তাই আমরা নিজে ফাইলটি মুছে ফেলার আশা করতে পারি না। উদাহরণস্বরূপ, foo ফাইলটি বারটি শেষ করার আগে মুছে ফেললে বারটির এখন একটি অবৈধ পয়েন্টার রয়েছে।

সুতরাং, আপনি যেমন অনুমান করতে পারেন, আমরা আমাদের সাহায্য করতে স্মার্ট পয়েন্টার ব্যবহার করতে পারি।

void setLog(const Foo & foo, const Bar & bar) {
    shared_ptr<File> file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

এখন, কারও ফাইল মুছে ফেলার বিষয়ে চিন্তা করার দরকার নেই - একবার ফু ও বার উভয়ই শেষ হয়ে গেলে এবং ফাইলটির আর কোনও উল্লেখ নেই (সম্ভবত ফু ও বার নষ্ট হওয়ার কারণে) ফাইলটি স্বয়ংক্রিয়ভাবে মুছে ফেলা হবে।


7
এটি লক্ষ করা উচিত যে অনেক স্ট্রিং বাস্তবায়ন রেফারেন্স গণনা পয়েন্টারের ক্ষেত্রে প্রয়োগ করা হয়। এই অনুলিপি-লেখার শব্দার্থক শব্দগুলি একটি স্ট্রিংকে মূল্য দিয়ে সত্যই সাশ্রয়ী করে তোলে।

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

27
স্ট্রিং ফিরিয়ে দেওয়া সত্যিই স্মার্ট পয়েন্টার ব্যবহারের জন্য ভাল কারণ নয়। রিটার্ন মান অপ্টিমাইজেশন সহজেই রিটার্নটিকে অপ্টিমাইজ করতে পারে এবং সি ++ 1x সরানো শব্দার্থক শব্দগুলি একটি অনুলিপি সম্পূর্ণভাবে মুছে ফেলবে (যখন সঠিকভাবে ব্যবহৃত হবে)। পরিবর্তে কিছু বাস্তব বিশ্বের উদাহরণ দেখান (উদাহরণস্বরূপ যখন আমরা একই সংস্থানটি ভাগ করি) :)
জোহানেস স্কাউব - lit 22

1
আমি মনে করি যে জাভা কেন এটি করতে পারে না তা নিয়ে আপনার উপসংহারের প্রথমদিকে স্পষ্টতা নেই। জাভা বা সি # তে এই সীমাবদ্ধতাটি বর্ণনা করার সবচেয়ে সহজ উপায় কারণ স্ট্যাকের উপর বরাদ্দ দেওয়ার কোনও উপায় নেই। সি # একটি বিশেষ কীওয়ার্ডের মাধ্যমে স্ট্যাক বরাদ্দকে মঞ্জুরি দেয় তবে আপনি টাইপ সাফতে হারাবেন।
অ্যাপলপিসআইডুড

4
@ নিমঞ্জা ত্রিফুনোভিচ: এই প্রসঙ্গে রাইআইআইয়ের অর্থ আপনার অনুলিপিগুলি ফিরিয়ে দেওয়া / স্ট্যাকের উপর বস্তু তৈরি করা? আপনার যদি উপশৃঙ্খলাবদ্ধ হতে পারে এমন ধরণের জিনিসগুলি ফেরত / গ্রহণ করে তা কার্যকর হয় না। তারপরে আপনাকে অবজেক্টটি কাটা এড়ানোর জন্য একটি পয়েন্টার ব্যবহার করতে হবে এবং আমি যুক্তি দেব যে স্মার্ট পয়েন্টারটি প্রায়শই সেই ক্ষেত্রে কাঁচা চেয়ে ভাল হয়।
ফ্র্যাঙ্ক ওস্টারফেল্ড

141

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

এসবিআরএম ছাড়াই এটি করার একটি উদাহরণ:

void o_really() {
     resource * r = allocate_resource();
     try {
         // something, which could throw. ...
     } catch(...) {
         deallocate_resource(r);
         throw;
     }
     if(...) { return; } // oops, forgot to deallocate
     deallocate_resource(r);
}

আপনি দেখতে পাচ্ছেন যে অনেকগুলি উপায় রয়েছে আমরা বন্ধ হয়ে যেতে পারি। ধারণাটি হ'ল আমরা রিসোর্স ম্যানেজমেন্টকে একটি শ্রেণিতে আবদ্ধ করি। এর অবজেক্টের সূচনাটি সংস্থানটি অর্জন করে ("রিসোর্স অ্যাকুইজিশন ইজ ইনিশিয়ালাইজেশন")। যখন আমরা ব্লকটি (ব্লক স্কোপ) থেকে প্রস্থান করি, সেই সময় পুনরায় সংস্থানটি মুক্ত হয়।

struct resource_holder {
    resource_holder() {
        r = allocate_resource();
    }
    ~resource_holder() {
        deallocate_resource(r);
    }
    resource * r;
};

void o_really() {
     resource_holder r;
     // something, which could throw. ...
     if(...) { return; }
}

এটি যদি আপনি তাদের নিজস্ব ক্লাসগুলি পেয়ে থাকেন যা কেবলমাত্র সম্পদ বরাদ্দ / অবনতির উদ্দেশ্যে নয়। বরাদ্দ তাদের কাজটি করার জন্য কেবল একটি অতিরিক্ত উদ্বেগ হবে। তবে যত তাড়াতাড়ি আপনি কেবল সংস্থানগুলি / ডিএলোকট রিসোর্স বরাদ্দ করতে চান, উপরেরটি অমান্য হয়ে যায়। আপনার প্রাপ্ত প্রতিটি ধরণের সংস্থানগুলির জন্য আপনাকে একটি মোড়ক দেওয়ার ক্লাস লিখতে হবে। এটি সহজ করার জন্য, স্মার্ট পয়েন্টারগুলি আপনাকে সেই প্রক্রিয়াটি স্বয়ংক্রিয় করতে দেয়:

shared_ptr<Entry> create_entry(Parameters p) {
    shared_ptr<Entry> e(Entry::createEntry(p), &Entry::freeEntry);
    return e;
}

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

বিভিন্ন উদ্দেশ্যে বিভিন্ন স্মার্ট পয়েন্টার রয়েছে:

unique_ptr

একটি স্মার্ট পয়েন্টার যা একচেটিয়াভাবে কোনও বস্তুর মালিক। এটি উত্সাহ দেয় না, তবে সম্ভবত এটি পরবর্তী সি ++ স্ট্যান্ডার্ডে উপস্থিত হবে। এটি অনুলিপিযোগ্য নয় তবে মালিকানার স্থানান্তরকে সমর্থন করে । কিছু উদাহরণ কোড (পরবর্তী সি ++):

কোড:

unique_ptr<plot_src> p(new plot_src); // now, p owns
unique_ptr<plot_src> u(move(p)); // now, u owns, p owns nothing.
unique_ptr<plot_src> v(u); // error, trying to copy u

vector<unique_ptr<plot_src>> pv; 
pv.emplace_back(new plot_src); 
pv.emplace_back(new plot_src);

অটো_পিটারের বিপরীতে, অনন্য_পিটার একটি পাত্রে রাখা যেতে পারে, কারণ ধারকগুলি অন-অনুলিপি (তবে চলমান) প্রকারগুলি যেমন স্ট্রিম এবং অনন্য_প্টরকে ধরে রাখতে সক্ষম হবে।

scoped_ptr

এটি একটি উত্সাহী স্মার্ট পয়েন্টার যা কপিরাইটযোগ্য বা চলনযোগ্যও নয়। সুযোগের বাইরে যাওয়ার সময় পয়েন্টার মোছার বিষয়টি নিশ্চিত করতে চাইলে এটি ব্যবহার করার উপযুক্ত জিনিস।

কোড:

void do_something() {
    scoped_ptr<pipe> sp(new pipe);
    // do something here...
} // when going out of scope, sp will delete the pointer automatically. 

shared_ptr

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

কোড:

shared_ptr<plot_src> p(new plot_src(&fx));
plot1->add(p)->setColor("#00FF00");
plot2->add(p)->setColor("#FF0000");
// if p now goes out of scope, the src won't be freed, as both plot1 and 
// plot2 both still have references. 

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


আমি যতদূর জানি নন-অনুলিপিযোগ্য বস্তুগুলি স্টেন্টের পাত্রে কোনওভাবে ব্যবহার করা ভাল না কারণ তারা মান শব্দার্থবিদ্যার উপর নির্ভর করে - আপনি যদি সেই ধারকটি বাছাই করতে চান তবে কী হবে? সাজানোর উপাদানগুলি অনুলিপি করে ...
fmuecke

সি ++ 0 এক্স পাত্রে পরিবর্তন করা হবে যাতে এটি কেবল চলমান ধরণের ক্ষেত্রে সম্মান করে unique_ptrএবং sortএকইভাবে পরিবর্তন করাও যায়।
জোহানেস স্কাউব -

আপনার মনে আছে আপনি এসবিআরএম শব্দটি প্রথম কোথায় শুনেছেন? জেমস এটি ট্র্যাক করার চেষ্টা করছে।
GManNickG

এগুলি ব্যবহারের জন্য আমার কোন শিরোনাম বা লাইব্রেরি অন্তর্ভুক্ত করা উচিত? এ নিয়ে আর কোন পাঠ?
atoMerz

এখানে একটি পরামর্শ: যদি @ লিটিব দ্বারা কোনও সি ++ প্রশ্নের উত্তর পাওয়া যায়, তবে এটি সঠিক উত্তর (ভোট বা উত্তর "সঠিক" হিসাবে চিহ্নিত করা হোক না কেন) ...
fnl

32

ধারণা এবং কারণগুলি সাধারণ, সাধারণ।

RAII হ'ল নকশা দৃষ্টান্ত এটি নিশ্চিত করার জন্য যে ভেরিয়েবলগুলি তাদের নির্মাতাদের সমস্ত প্রয়োজনীয় আরম্ভ এবং তাদের ডেস্ট্রাক্টরগুলিতে সমস্ত প্রয়োজনীয় ক্লিনআপ পরিচালনা করে। এটি এক এক ধাপে সমস্ত সূচনা এবং পরিষ্কারকরণ হ্রাস করে।

সি ++ এর জন্য আরআইআইআইয়ের প্রয়োজন হয় না, তবে এটি ক্রমবর্ধমানভাবে গ্রহণযোগ্য যে RAII পদ্ধতিগুলি ব্যবহার করার ফলে আরও শক্তিশালী কোড তৈরি হবে।

আরআইআই সি ++ এ দরকারী যে কারণটি হ'ল সি ++ ভেরিয়েবলগুলি প্রবেশের সময় এবং সুযোগটি প্রবেশের সাথে সাথে অভ্যন্তরীণভাবে তৈরি এবং ধ্বংস পরিচালনা করে, কোনও ব্যতিক্রমের দ্বারা সাধারণ কোড প্রবাহের মাধ্যমে বা স্টাইন্ড আনওয়াইন্ডিং দিয়ে শুরু করে। এটি সি ++ এ একটি ফ্রিবি।

এই প্রক্রিয়াগুলিতে সমস্ত সূচনা এবং ক্লিনআপ বেঁধে, আপনি নিশ্চিত হন যে সি ++ আপনার জন্যও এই কাজের যত্ন নেবে।

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


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

8

স্মার্ট পয়েন্টারটি RAII এর একটি প্রকরণ। আরআইআই মানে রিসোর্স অধিগ্রহণ হ'ল সূচনা। স্মার্ট পয়েন্টার ব্যবহারের আগে একটি সংস্থান (মেমরি) অর্জন করে এবং তারপরে এটি কোনও ডিস্ট্রাক্টরে স্বয়ংক্রিয়ভাবে ফেলে দেয়। দুটি জিনিস ঘটে:

  1. আমরা মেমরিটি ব্যবহারের আগে সর্বদা বজায় রাখি, যখন আমরা এটির মতো অনুভব করি না তখনও - স্মার্ট পয়েন্টারটির সাহায্যে অন্য কোনও উপায়ে কাজ করা শক্ত। যদি এটি না ঘটে থাকে তবে আপনি NULL স্মৃতি অ্যাক্সেস করার চেষ্টা করবেন, ক্রাশের ফলে (খুব বেদনাদায়ক)।
  2. ত্রুটি থাকলেও আমরা মেমরি মুক্ত করি । কোনও স্মৃতি ঝুলে থাকে না।

উদাহরণস্বরূপ, আরেকটি উদাহরণ হ'ল নেটওয়ার্ক সকেট আরআইআইআই। এক্ষেত্রে:

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

এখন, আপনি দেখতে পাচ্ছেন, বেশিরভাগ ক্ষেত্রে RAII একটি খুব দরকারী সরঞ্জাম কারণ এটি মানুষকে পাথর কাটাতে সহায়তা করে।

সি ++ স্মার্ট পয়েন্টারগুলির উত্স আমার উপরে প্রতিক্রিয়া সহ নেটটির আশেপাশে কয়েক মিলিয়নে রয়েছে।


2

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


0
অকার্যকর foo ()
{
   std :: স্ট্রিং বার;
   //
   // আরও কোড এখানে
   //
}

যাই ঘটুক না কেন, বারে foo () ফাংশনটির সুযোগটি রেখে দেওয়া হলে বারটি সঠিকভাবে মুছে ফেলা হবে।

অভ্যন্তরীণ std :: স্ট্রিং বাস্তবায়ন প্রায়শই রেফারেন্স গণনা পয়েন্টার ব্যবহার করে। সুতরাং কেবলমাত্র অভ্যন্তরীণ স্ট্রিংটি অনুলিপি করা দরকার যখন তারগুলির একটি অনুলিপি পরিবর্তন হয়। অতএব স্মার্ট পয়েন্টার হিসাবে গণ্য হওয়া একটি রেফারেন্স কেবলমাত্র যখন প্রয়োজন তখন কিছু অনুলিপি করা সম্ভব করে তোলে।

তদতিরিক্ত, অভ্যন্তরীণ রেফারেন্স গণনা এটি সম্ভব করে তোলে যে অভ্যন্তরীণ স্ট্রিংয়ের অনুলিপিটির আর প্রয়োজন নেই হলে মেমরিটি সঠিকভাবে মুছে ফেলা হবে।


1
অকার্যকর চ () {ওবজ এক্স; } ওজজ এক্স স্ট্যাক ফ্রেম তৈরি / ধ্বংসের মাধ্যমে (মুছে ফেলা হয় না) মুছে ফেলা হয় ... এটি রেফ গণনার সাথে সম্পর্কিত নয়।
হার্নান

রেফারেন্স গণনা স্ট্রিংয়ের অভ্যন্তরীণ প্রয়োগের একটি বৈশিষ্ট্য। RAII হ'ল অবজেক্টটি মুছে ফেলার পেছনের ধারণা the প্রশ্নটি ছিল আরআইআই এবং স্মার্ট পয়েন্টার সম্পর্কেও।

1
"যাই ঘটুক না কেন" - ফাংশন ফেরার আগে কোনও ব্যতিক্রম ছুঁড়ে ফেলা হলে কী হয়?
টাইটানিয়ামডেকয়

কোন ফাংশন ফিরিয়ে দেওয়া হয়? যদি বারে মুছে ফেলা হয় তার চেয়ে ব্যতিক্রম যদি foo এ ফেলে দেওয়া হয়। ব্যতিক্রম নিক্ষেপকারী ডিফল্ট নির্মাতা একটি অসাধারণ ঘটনা হবে।
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.