std::unique_ptr
ক্লাস টেমপ্লেটের উদাহরণ দিয়ে যার স্মৃতি পরিচালিত হয় সেই বস্তুর কাছে পয়েন্টারগুলি পাশ করার বিভিন্ন কার্যকর পদ্ধতিগুলি বলার চেষ্টা করি ; এটি পুরানো std::auto_ptr
শ্রেণির টেম্পলেটটিতেও প্রযোজ্য (যা আমি বিশ্বাস করি যে অনন্য পয়েন্টার ব্যবহার করে এমন সমস্ত ব্যবহারের অনুমতি দেয় তবে এর জন্য পরিবর্তিত ল্যাভেলুগুলি গ্রহণ করা হবে যেখানে মূল্যবোধ করা উচিত নয় std::move
) এবং কিছুটা হলেও std::shared_ptr
।
আলোচনার একটি দৃ concrete় উদাহরণ হিসাবে আমি নিম্নলিখিত সহজ তালিকার ধরণটি বিবেচনা করব
struct node;
typedef std::unique_ptr<node> list;
struct node { int entry; list next; }
এই জাতীয় তালিকার উদাহরণগুলি (যা অন্যান্য উদাহরণগুলির সাথে অংশগুলি ভাগ করে নেওয়ার বা বিজ্ঞপ্তিযুক্ত হতে দেওয়া যায় না) পুরোপুরি যারাই প্রাথমিক list
পয়েন্টারটি ধারণ করে তার মালিকানাধীন । যদি ক্লায়েন্ট কোডটি জানে যে এটি তালিকাটি সঞ্চয় করে তা কখনই খালি থাকে না, এটি এ এর node
পরিবর্তে সরাসরি সরাসরি সঞ্চয় করতেও পছন্দ করতে পারে list
। কোনও ডেস্ট্রাক্টরকে node
সংজ্ঞায়িত করতে হবে না: যেহেতু এর ক্ষেত্রগুলির জন্য ডেস্ট্রাক্টরগুলি স্বয়ংক্রিয়ভাবে ডাকা হয়, তাই প্রাথমিক পয়েন্টার বা নোডের জীবনকাল শেষ হওয়ার পরে পুরো তালিকাটি স্মার্ট পয়েন্টার ডেস্ট্রাক্টর দ্বারা পুনরাবৃত্তভাবে মুছে ফেলা হবে।
এই পুনরাবৃত্তির ধরণটি এমন কিছু ক্ষেত্রে আলোচনা করার উপলক্ষ দেয় যা সাধারণ তথ্যতে স্মার্ট পয়েন্টারের ক্ষেত্রে কম দেখা যায় less এছাড়াও ফাংশনগুলি নিজেরাই মাঝে মধ্যে ক্লায়েন্ট কোডের একটি উদাহরণ প্রদান করে (পুনরাবৃত্তভাবে)। টাইপিডেফ list
অবশ্যই পক্ষপাতদুষ্ট unique_ptr
, তবে সংজ্ঞাটি ব্যবহারের পরিবর্তে auto_ptr
বা shared_ptr
পরিবর্তে নীচে যা বলা হয়েছে তার পরিবর্তনের প্রয়োজন নেই (বিশেষত ব্যতিক্রমী সুরক্ষার বিষয়ে ডেস্ট্রাক্টরদের লেখার প্রয়োজন ছাড়াই আশ্বাস দেওয়া হচ্ছে)।
চারপাশে স্মার্ট পয়েন্টারগুলি পাশ করার মোডগুলি
মোড 0: স্মার্ট পয়েন্টারের পরিবর্তে একটি পয়েন্টার বা রেফারেন্স আর্গুমেন্টটি পাস করুন
যদি আপনার ফাংশন মালিকানা নিয়ে উদ্বিগ্ন না হয় তবে এটি পছন্দসই পদ্ধতি: এটিকে স্মার্ট পয়েন্টারটি মোটেও তুলবেন না। এই ক্ষেত্রে আপনার ফাংশন চিন্তা করার দরকার নেই যারা , মালিক বস্তুর প্রতি ইঙ্গিত, বা কি এর মানে হল যে মালিকানা পরিচালিত হয়, তাই ক্ষণস্থায়ী একটি কাঁচা পয়েন্টার উভয় পুরোপুরি নিরাপদ এবং অধিকাংশ নমনীয় ফর্ম মালিকানা নির্বিশেষে যেহেতু একটি ক্লায়েন্ট সবসময় করতে পারেন একটি কাঁচা পয়েন্টার উত্পাদন করুন ( get
পদ্ধতিটি কল করে বা অপারেটরের ঠিকানা থেকে &
)।
উদাহরণস্বরূপ এই জাতীয় তালিকার দৈর্ঘ্য গণনা করার জন্য কার্যটি একটি list
যুক্তি দেওয়া উচিত নয় , তবে একটি কাঁচা পয়েন্টার দেওয়া উচিত:
size_t length(const node* p)
{ size_t l=0; for ( ; p!=nullptr; p=p->next.get()) ++l; return l; }
কোনও ক্লায়েন্ট যা একটি ভেরিয়েবল রাখে list head
এই ফাংশনটি হিসাবে কল করতে পারে length(head.get())
, যখন কোনও ক্লায়েন্ট যে node n
শূন্যস্থান না করে খালি তালিকা উপস্থাপনের পরিবর্তে চয়ন করেছে সে কল করতে পারে length(&n)
।
যদি পয়েন্টারটি নন নাল হওয়ার গ্যারান্টিযুক্ত হয় (তবে এখানে তালিকাটি খালি থাকতে পারে) তবে পয়েন্টারের পরিবর্তে একটি রেফারেন্স পাস করতে পছন্দ করতে পারে। এটি অ-পয়েন্টার / রেফারেন্স হতে পারে const
যদি ফাংশনটিতে নোড (গুলি) এর বিষয়বস্তুগুলির কোনও আপডেট না করে বা অপসারণ না করে আপডেট করা দরকার (তবে তার মালিকানা জড়িত থাকবে)।
মোড 0 বিভাগে আসা একটি আকর্ষণীয় কেস তালিকার একটি (গভীর) অনুলিপি তৈরি করছে; এটির কোনও ক্রিয়াকলাপ অবশ্যই তৈরি করা অনুলিপিটির মালিকানা হস্তান্তর করতে হবে, তবে এটি অনুলিপি করা তালিকার মালিকানার সাথে সম্পর্কিত নয়। সুতরাং এটি নিম্নলিখিত হিসাবে সংজ্ঞায়িত করা যেতে পারে:
list copy(const node* p)
{ return list( p==nullptr ? nullptr : new node{p->entry,copy(p->next.get())} ); }
এই কোড যথার্থতা ঘনিষ্ঠ বর্ণন উভয় কেন এটা সব সময়ে (recursive কল ফলাফলের সাথে প্রাসঙ্গিক প্রনয়ন হিসেবে প্রশ্নের জন্য copy
initialiser তালিকা শুশ্রূষা এর পদক্ষেপ কন্সট্রাকটর মধ্যে rvalue রেফারেন্স যুক্তি unique_ptr<node>
, ওরফে list
, যখন initialising next
ক্ষেত্রে উত্পন্ন node
), এবং কেন এটি ব্যতিক্রম-নিরাপদ এই প্রশ্নের জন্য (যদি পুনরাবৃত্ত বরাদ্দ প্রক্রিয়া চলাকালীন মেমরি চলে এবং কিছু new
ছোঁড়ার ডাক পড়ে std::bad_alloc
, তবে সেই সময় আংশিকভাবে নির্মিত তালিকার একটি পয়েন্টার একটি অস্থায়ীভাবে বেনামে রাখা হয় list
প্রারম্ভিক তালিকার জন্য তৈরি করা হয়েছে এবং এর ডেস্ট্রাক্টর সেই আংশিক তালিকাটি পরিষ্কার করে দেবে)। উপায় দ্বারা প্রতিস্থাপন করার প্রলোভনটি প্রতিরোধ করা উচিত (প্রথমদিকে আমি যেমন করেছি) দ্বিতীয় nullptr
দ্বারাp
যা সর্বোপরি সেই মুহুর্তে শূন্য বলে জানা যায়: নাল হিসাবে পরিচিত হয়েও কেউ একটি (কাঁচা) পয়েন্টার থেকে ধ্রুবক পর্যন্ত স্মার্ট পয়েন্টার তৈরি করতে পারে না ।
মোড 1: মান অনুসারে একটি স্মার্ট পয়েন্টার পাস করুন
একটি ফাংশন যা একটি স্মার্ট পয়েন্টার মানটি আর্গুমেন্ট হিসাবে গ্রহণ করে তা অবিলম্বে নির্দেশিত অবজেক্টটির দখল নেয়: কলারটি যে স্মার্ট পয়েন্টারটি ধারণ করেছিল (কোনও নামকরণ ভেরিয়েবল হোক বা বেনামে অস্থায়ী হোক) ফাংশনের প্রবেশদ্বারে আর্গুমেন্ট ভ্যালুতে অনুলিপি করা হয় এবং কলারের পয়েন্টারটি নাল হয়ে গেছে (অস্থায়ী ক্ষেত্রে কপিটি অন্যদিকে থাকতে পারে, তবে কোনও ক্ষেত্রে কলার পয়েন্ট টু অবজেক্টের অ্যাক্সেস হারিয়ে ফেলেছে)। আমি নগদ দ্বারা এই মোড কলটিতে কল করতে চাই : কলার কল করা পরিষেবাটির জন্য অগ্রিম অর্থ প্রদান করে এবং কল করার পরে মালিকানা সম্পর্কে কোনও বিভ্রান্তি থাকতে পারে না। এটি পরিষ্কার করার জন্য, ভাষা বিধিগুলির জন্য কলারটিকে যুক্তিটি মোড়ানো দরকারstd::move
যদি স্মার্ট পয়েন্টারটি একটি চলকতে রাখা হয় (প্রযুক্তিগতভাবে, যদি আর্গুমেন্টটি লভ্যালু হয়); এক্ষেত্রে (তবে নীচে 3 মোডের জন্য নয়) এই ফাংশনটি তার নামটির পরামর্শ অনুসারে যা করে, ভেরিয়েবলটি নাল রেখে ভেরিয়েবল থেকে মানটিকে একটি অস্থায়ী স্থানে নিয়ে যায়।
কথিত ফাংশনটি নিঃশর্তভাবে পাইলট-টু অবজেক্টের (পাইলটারদের) মালিকানা গ্রহণ করে এমন ক্ষেত্রে, এই মোডটি তার মালিকানার সাথে পয়েন্টারটি একত্রে পাস করার একটি ভাল উপায় std::unique_ptr
বা std::auto_ptr
মেমরি ফাঁসের কোনও ঝুঁকি এড়ায়। তবুও আমি মনে করি যে খুব কম পরিস্থিতি রয়েছে যেখানে নীচে মোড 3 টি মোডের চেয়ে বেশি পছন্দ করা উচিত নয় (কখনও কখনও সামান্য) মোডের চেয়ে বেশি this এই কারণে আমি এই মোডের কোনও ব্যবহারের উদাহরণ সরবরাহ করব না। (তবে reversed
নীচে 3 মোডের উদাহরণ দেখুন, যেখানে এটি চিহ্নিত হয়েছে যে মোড 1 কমপক্ষে পাশাপাশি করবে)) যদি ফাংশনটি কেবল এই পয়েন্টারটির চেয়ে বেশি যুক্তি নেয় তবে এটি ঘটতে পারে যে মোড এড়াতে কোনও প্রযুক্তিগত কারণ ছাড়াও থাকতে পারে 1 (সাথে std::unique_ptr
বা std::auto_ptr
): যেহেতু একটি পয়েন্টার ভেরিয়েবল পাস করার সময় একটি আসল পদক্ষেপ অপারেশন হয়p
অভিব্যক্তি দ্বারা std::move(p)
, এটি p
ধরে নেওয়া যায় না যে অন্যান্য তর্কগুলি মূল্যায়নের সময় একটি কার্যকর মূল্য রয়েছে (মূল্যায়নের ক্রমটিকে অনির্দিষ্ট করা হচ্ছে), যা সূক্ষ্ম ত্রুটির দিকে পরিচালিত করতে পারে; বিপরীতে, 3 মোড ব্যবহার করে নিশ্চিত করা হয় যে p
ফাংশন কলের আগে কোনও পদক্ষেপ নেওয়া হবে না, সুতরাং অন্যান্য যুক্তিগুলি নিরাপদে কোনও মান অ্যাক্সেস করতে পারে p
।
std::shared_ptr
এটির সাথে ব্যবহার করার সময় , এই মোডটি আকর্ষণীয় যে এতে একটি একক ফাংশন সংজ্ঞা রয়েছে যা কলকারীটিকে ফাংশনটি ব্যবহার করার জন্য একটি নতুন ভাগ করে নেওয়ার অনুলিপি তৈরি করার সময় পয়েন্টারের একটি ভাগ করার অনুলিপি নিজের কাছে রাখবে কিনা তা চয়ন করতে দেয় (যখন এটি কোনও মূল্যমান হয় তখন ঘটে থাকে) আর্গুমেন্ট সরবরাহ করা হয়; কলটিতে ব্যবহৃত ভাগ করা পয়েন্টারগুলির জন্য অনুলিপি নির্ধারক রেফারেন্স গণনা বাড়িয়ে তোলে), বা কেবল ফাংশনটিকে পয়েন্টারটির একটি অনুলিপি না দিয়ে বা রেফারেন্স গণনাটি স্পর্শ না করে (কোনও তাত্ক্ষণিক যুক্তি সরবরাহ করা হয়, সম্ভবত এটি ঘটে একটি lvalue একটি কলে আবৃত std::move
)। এই ক্ষেত্রে
void f(std::shared_ptr<X> x) // call by shared cash
{ container.insert(std::move(x)); } // store shared pointer in container
void client()
{ std::shared_ptr<X> p = std::make_shared<X>(args);
f(p); // lvalue argument; store pointer in container but keep a copy
f(std::make_shared<X>(args)); // prvalue argument; fresh pointer is just stored away
f(std::move(p)); // xvalue argument; p is transferred to container and left null
}
একইভাবে পৃথকভাবে void f(const std::shared_ptr<X>& x)
(লভালু কেসের জন্য) সংজ্ঞা প্রদান করে এবং void f(std::shared_ptr<X>&& x)
(মূল্যমানের ক্ষেত্রে) ফাংশন বডিগুলির সাথে পৃথক পৃথক পৃথক পৃথক সংস্করণ কেবল একই সংস্করণে অনুলিপি তৈরি করতে পারে (কপি নির্মাণ / ব্যবহারের সময় অ্যাসাইনমেন্ট ব্যবহার করে x
) তবে দ্বিতীয় সংস্করণ সরানো শব্দার্থিক ( std::move(x)
পরিবর্তে উদাহরণ, কোড কোড হিসাবে)। সুতরাং ভাগ করা পয়েন্টারগুলির জন্য, কিছু কোডের নকল এড়াতে মোড 1 কার্যকর হতে পারে be
মোড 2: (সংশোধনযোগ্য) লভ্যালু রেফারেন্স দিয়ে একটি স্মার্ট পয়েন্টার পাস করুন
এখানে ফাংশনটির জন্য কেবল স্মার্ট পয়েন্টারটির একটি পরিবর্তিতযোগ্য রেফারেন্স থাকা দরকার তবে এটি এর সাথে কী করবে তা কোনও ইঙ্গিত দেয় না। আমি কার্ডের মাধ্যমে এই পদ্ধতিটিতে কল করতে চাই : কলকারী একটি ক্রেডিট কার্ড নম্বর দিয়ে অর্থ প্রদানের নিশ্চয়তা দেয়। রেফারেন্সটি পয়েন্ট-টু অবজেক্টের মালিকানা নিতে ব্যবহার করা যেতে পারে, তবে এটি করার দরকার নেই। এই মোডটির জন্য একটি পরিবর্তনযোগ্য লভ্যালু আর্গুমেন্ট সরবরাহ করা প্রয়োজন, ফাংশনের কাঙ্ক্ষিত প্রভাবটি যুক্তি ভেরিয়েবলের একটি কার্যকর মান রেখে অন্তর্ভুক্ত থাকতে পারে। মূলত অভিব্যক্তিযুক্ত একজন কলকারী যে এই জাতীয় ফাংশনটিতে যেতে চান এটি কল করতে সক্ষম হওয়ার জন্য এটি একটি নামযুক্ত ভেরিয়েবলের মধ্যে সঞ্চয় করতে বাধ্য হবে, যেহেতু ভাষা কেবল একটি ধ্রুবককে অন্তর্নিহিত রূপান্তর সরবরাহ করে sinceএকটি মূল্য থেকে রেভেলু রেফারেন্স (একটি অস্থায়ী উল্লেখ করে)। (বিপরীত অবস্থা দ্বারা পরিচালিত ভিন্ন std::move
, থেকে একটি ঢালাই Y&&
করার Y&
সঙ্গে Y
স্মার্ট পয়েন্টার ধরন, সম্ভব নয়; তা সত্ত্বেও এই রূপান্তর একটি সহজ ফর্মা ফাংশন যদি সত্যিই আকাঙ্ক্ষিত দ্বারা প্রাপ্ত করা যেতে পারে; দেখতে https://stackoverflow.com/a/24868376 / 1436796 )। যে ক্ষেত্রে কথিত ফাংশনটি নিঃশর্তভাবে অবজেক্টটির মালিকানা নিতে চায়, তর্ক থেকে চুরি করে, একটি মূল্যবান যুক্তি সরবরাহ করার বাধ্যবাধকতাটি ভুল সংকেত দিচ্ছে: কল করার পরে ভেরিয়েবলের কোনও কার্যকর মান থাকবে না। সুতরাং মোড 3, যা আমাদের ফাংশনের অভ্যন্তরে অভিন্ন সম্ভাবনা দেয় তবে কলারদের একটি মূল্যায়ন সরবরাহ করতে বলে, এই ধরনের ব্যবহারের জন্য পছন্দ করা উচিত।
তবে মোড 2 এর বৈধ ব্যবহারের কেস রয়েছে , পয়েন্টারটি সংশোধন করতে পারে এমন ফাংশনগুলি বা অবজেক্টটি এমনভাবে নির্দেশিত হয়েছে যাতে মালিকানা জড়িত । উদাহরণস্বরূপ, একটি ফাংশন যা কোনও নোডের সাথে পূর্বের list
উপস্থাপন করে এমন ব্যবহারের একটি উদাহরণ সরবরাহ করে:
void prepend (int x, list& l) { l = list( new node{ x, std::move(l)} ); }
স্পষ্টতই এখানে কলকারীদের ব্যবহার করতে বাধ্য করা অনাকাঙ্ক্ষিত হবে std::move
কারণ তাদের স্মার্ট পয়েন্টারটি কলটির পরে এখনও একটি ভাল সংজ্ঞায়িত এবং খালি খালি তালিকার মালিক, যদিও আগের চেয়ে আলাদা one
আবার prepend
কলটি ফ্রি মেমরির অভাবে ব্যর্থ হলে কী ঘটে তা পর্যবেক্ষণ করা আকর্ষণীয় । তারপরে new
কল ছুড়ে দেবে std::bad_alloc
; এই মুহুর্তে, যেহেতু কোনও node
বরাদ্দ দেওয়া যায়নি, এটি নিশ্চিত যে বরাদ্দ ব্যর্থতার ক্ষেত্রটি std::move(l)
নির্মাণের জন্য এটি করা হবে বলে এখনও পাস করা রুল্যু রেফারেন্স (মোড 3) এখনও পাইলফ করা যায়নি । সুতরাং ত্রুটি নিক্ষেপ করা হলে মূল স্মার্ট পয়েন্টারটি এখনও মূল তালিকা ধরে রাখে; সেই তালিকাটি হয় স্মার্ট পয়েন্টার ডেস্ট্রাক্টর দ্বারা যথাযথভাবে ধ্বংস হয়ে যাবে, বা পর্যাপ্ত প্রারম্ভিক ধারাটির কারণে যদি বেঁচে থাকতে পারে তবে এটি এখনও মূল তালিকাটি ধারণ করবে।next
node
l
l
catch
এটি একটি গঠনমূলক উদাহরণ ছিল; এই প্রশ্নের চোখের সামনে কেউ কোনও প্রদত্ত মান সম্বলিত প্রথম নোড অপসারণের আরও ধ্বংসাত্মক উদাহরণ দিতে পারে, যদি কোনও:
void remove_first(int x, list& l)
{ list* p = &l;
while ((*p).get()!=nullptr and (*p)->entry!=x)
p = &(*p)->next;
if ((*p).get()!=nullptr)
(*p).reset((*p)->next.release()); // or equivalent: *p = std::move((*p)->next);
}
আবার সঠিকতা এখানে বেশ সূক্ষ্ম। উল্লেখযোগ্যভাবে, চূড়ান্ত বিবৃতিতে (*p)->next
নোডের অপসারণের জন্য রাখা পয়েন্টারটি লিঙ্কযুক্ত করে (দ্বারা release
, যা পয়েন্টারটি দেয় কিন্তু মূল নাল করে দেয়) পূর্বে reset
(স্পষ্টভাবে) সেই নোডকে ধ্বংস করে (যখন এটি রাখা পুরানো মানটিকে ধ্বংস করে p
), নিশ্চিত করে একটি এবং শুধুমাত্র একটি নোড সেই সময়ে ধ্বংস হয়। (বিকল্প মন্তব্যে উল্লেখ ফর্ম, এই টাইমিং সরান নিয়োগ অপারেটর প্রয়োগের অভ্যন্তরীণ বামে হবে std::unique_ptr
উদাহরণস্বরূপ list
; মান 20.7.1.2.3 বলছেন; 2 যে এই অপারেটর হিসাবে দ্বারা যদি "কাজ করা উচিত কলিং reset(u.release())
", যেহেতু এখানেও সময়টি নিরাপদ থাকা উচিত))
নোট করুন prepend
এবং remove_first
এমন ক্লায়েন্টরা কল করতে পারবেন না যারা node
সর্বদা খালি খালি তালিকার জন্য স্থানীয় ভেরিয়েবল সঞ্চয় করে এবং ঠিক তাই, যেহেতু প্রদত্ত বাস্তবায়নগুলি এই ধরনের ক্ষেত্রে কাজ করতে পারে না।
মোড 3: (সংশোধনযোগ্য) রুল্যু রেফারেন্স দ্বারা একটি স্মার্ট পয়েন্টার পাস করুন
পয়েন্টারের মালিকানা গ্রহণ করার সময় এটি ব্যবহার করা পছন্দসই মোড। আমি এই পদ্ধতিতে চেক করে কল করতে চাই : কলকারীকে অবশ্যই নগদ প্রদানের ক্ষেত্রে, চেক স্বাক্ষরের মাধ্যমে ত্যাগের মালিকানা অবশ্যই মেনে নিতে হবে, তবে প্রকৃত প্রত্যাহার স্থগিত করা হবে যতক্ষণ না কথিত ফাংশনটি পয়েন্টারটি চালিত না করে (ঠিক একইভাবে মোড 2 ব্যবহার করার সময় হবে )। "চেকের স্বাক্ষর" কংক্রিটের অর্থ হ'ল কলারদের একটি যুক্তিটি std::move
মোড করতে হবে (মোড 1 এর মতো) যদি এটি মূল্য হয় (তবে এটি যদি কোনও মূল্যায়ন হয় তবে "মালিকানা ছেড়ে দেওয়া" অংশটি সুস্পষ্ট এবং কোনও পৃথক কোডের প্রয়োজন নেই)।
নোট করুন যে প্রযুক্তিগতভাবে মোড 3 মোড 2 হিসাবে ঠিক আচরণ করে, তাই তথাকথিত ফাংশনটির মালিকানা নিতে হবে না ; তবে আমি জিদ যে যদি সেখানে মালিকানা হস্তান্তর (স্বাভাবিক ব্যবহার) সম্পর্কে কোনো অনিশ্চয়তা, মোড 2 মোড থেকে 3 পছন্দের করা উচিত, যাতে মোড 3 ব্যবহার পরোক্ষভাবে কলারের যে, তারা একটি সংকেত হয় মালিকানা ছোড়। যে কেউ কেবল প্রতিক্রিয়া জানাতে পারে যে কেবল মোড 1 আর্গুমেন্ট সত্যই পাসকারীরা কলকারীদের মালিকানা হারাতে বাধ্য করে forced তবে যদি কোনও ক্লায়েন্টের কথিত ফাংশনটির উদ্দেশ্য সম্পর্কে কোনও সন্দেহ থাকে, তবে তাকে কল করা ফাংশনের বৈশিষ্ট্যগুলি জানা উচিত, যা কোনও সন্দেহ দূর করতে পারে।
list
মোড 3 আর্গুমেন্ট পাসিং ব্যবহার করে আমাদের ধরণের সাথে জড়িত একটি আদর্শ উদাহরণ পাওয়া অবাক করা কঠিন । b
অন্য তালিকার শেষে একটি তালিকা সরানো একটি a
সাধারণ উদাহরণ; তবে a
(যা অপারেশনের ফলাফলটি ধরে রেখেছে এবং ধরে রেখেছে) 2 মোড ব্যবহার করে আরও ভাল উত্তীর্ণ হবে:
void append (list& a, list&& b)
{ list* p=&a;
while ((*p).get()!=nullptr) // find end of list a
p=&(*p)->next;
*p = std::move(b); // attach b; the variable b relinquishes ownership here
}
মোড 3 আর্গুমেন্ট পাসের একটি খাঁটি উদাহরণ নিম্নলিখিতটি একটি তালিকা (এবং তার মালিকানা) নেয় এবং বিপরীত ক্রমে অভিন্ন নোডযুক্ত একটি তালিকা ফেরত দেয়।
list reversed (list&& l) noexcept // pilfering reversal of list
{ list p(l.release()); // move list into temporary for traversal
list result(nullptr);
while (p.get()!=nullptr)
{ // permute: result --> p->next --> p --> (cycle to result)
result.swap(p->next);
result.swap(p);
}
return result;
}
এই ফাংশনটি l = reversed(std::move(l));
তালিকাটিকে নিজের মধ্যে উল্টো করতে বলা হতে পারে , তবে বিপরীত তালিকাটিও আলাদাভাবে ব্যবহার করা যেতে পারে।
এখানে যুক্তিটি তাত্ক্ষণিক দক্ষতার জন্য স্থানীয় পরিবর্তনশীলে স্থানান্তরিত হয় (কেউ l
সরাসরি প্যারামিটারটির স্থানে ব্যবহার করতে পারত p
, তবে প্রতিটি সময় এটির অ্যাক্সেসে অতিরিক্ত পদক্ষেপের অন্তর্ভুক্ত থাকতে পারে); সুতরাং মোড 1 আর্গুমেন্ট পাসিংয়ের সাথে পার্থক্যটি ন্যূনতম। প্রকৃতপক্ষে সেই মোডটি ব্যবহার করে, যুক্তিটি সরাসরি স্থানীয় পরিবর্তনশীল হিসাবে পরিবেশন করতে পারে, ফলে প্রাথমিকভাবে এই পদক্ষেপটি এড়ানো; এটি কেবলমাত্র সাধারণ নীতির একটি উদাহরণ যে রেফারেন্স দ্বারা পাস হওয়া একটি আর্গুমেন্ট যদি কেবল স্থানীয় ভেরিয়েবলকে সূচনা করতে পারে তবে তার পরিবর্তে মান দ্বারা এটি পাস করা যায় এবং স্থানীয় ভেরিয়েবল হিসাবে প্যারামিটারটি ব্যবহার করা যেতে পারে।
3 মোড ব্যবহার করে স্ট্যান্ডার্ড দ্বারা সমর্থন করা হবে বলে মনে হয়, সমস্ত প্রদত্ত লাইব্রেরি ফাংশন যা মোড 3 ব্যবহার করে স্মার্ট পয়েন্টারগুলির মালিকানা হস্তান্তর করে point std::shared_ptr<T>(auto_ptr<T>&& p)
. বিন্দুতে একটি নির্দিষ্ট দৃinc়প্রত্যয়ী কেস কনস্ট্রাক্টর । যে কন্সট্রাক্টর (ব্যবহৃত std::tr1
) একটি সংশোধনযোগ্য নেওয়া lvalue রেফারেন্স (ঠিক মত auto_ptr<T>&
কপি কন্সট্রাকটর), এবং সেইজন্য একটি সঙ্গে বলা যেতে পারে auto_ptr<T>
lvalue p
হিসেবে std::shared_ptr<T> q(p)
, যার পরে p
নাল রিসেট করা হয়েছে। আর্গুমেন্ট পাসিংয়ে মোড 2 থেকে 3 এ পরিবর্তনের কারণে, এই পুরাতন কোডটি এখন আবার লিখতে হবে std::shared_ptr<T> q(std::move(p))
এবং তারপরে কাজ চালিয়ে যাবে। আমি বুঝতে পারি যে কমিটি এখানে 2 মোড পছন্দ করে না, তবে তাদের সংজ্ঞা দিয়ে তাদের 1 মোডে পরিবর্তনের বিকল্প ছিলstd::shared_ptr<T>(auto_ptr<T> p)
পরিবর্তে, তারা নিশ্চিত করতে পারত যে পুরানো কোডটি কোনও পরিবর্তন ছাড়াই কাজ করে, কারণ (অনন্য-পয়েন্টারগুলির বিপরীতে) অটো-পয়েন্টারগুলি নিঃশব্দে একটি মানকে চিহ্নিত করা যেতে পারে (পয়েন্টার অবজেক্টটি নিজেই প্রক্রিয়াটি শূন্য করতে পুনরায় সেট করা হচ্ছে)। স্পষ্টতই কমিটি মোড 1 এর চেয়ে 3 অ্যাডভোকেটিং মোডটিকে এত বেশি পছন্দ করেছে যে তারা ইতিমধ্যে অবমুক্ত ব্যবহারের জন্য এমনকি মোড 1 ব্যবহার করার পরিবর্তে বিদ্যমান কোডটি সক্রিয়ভাবে ভেঙে ফেলতে বেছে নিয়েছে ।
যখন মোড 3 ওভার মোড 1 পছন্দ করবেন
মোড 1 বেশ কয়েকটি ক্ষেত্রে পুরোপুরি ব্যবহারযোগ্য এবং reversed
উপরের উদাহরণ হিসাবে যেমন মালিকানা ধরে নেওয়া অন্যথায় স্মার্ট পয়েন্টারটিকে স্থানীয় ভেরিয়েবলের দিকে নিয়ে যাওয়ার রূপ নেয় তবে ক্ষেত্রে 3 এর চেয়ে বেশি পছন্দ করা যেতে পারে । তবে আমি আরও সাধারণ ক্ষেত্রে মোড 3 পছন্দ করার দুটি কারণ দেখতে পাচ্ছি:
কোনও অস্থায়ী তৈরি করা এবং পুরানো পয়েন্টারটি নিক্স করার চেয়ে নগদ পরিচালনা করা কিছুটা শ্রমসাধ্য হয় তুলনায় কিছুটা দক্ষ; কিছু দৃশ্যে পয়েন্টারটি অন্য কোনও ফাংশনে অপরিবর্তিত হওয়ার আগে এটি পাইলট করার আগে বেশ কয়েকবার অতিক্রম করা যেতে পারে। এ জাতীয় পাসিংয়ের জন্য সাধারণত লেখার প্রয়োজন হবে std::move
(মোড 2 ব্যবহার না করা অবধি) তবে লক্ষ করুন যে এটি কেবলমাত্র একটি castালাই যা আসলে কিছু করে না (বিশেষত কোনও ডেরেফারেন্সিং) না, তাই এটির সাথে শূন্য খরচ যুক্ত থাকে।
এটি কি অনুধাবনযোগ্য যে ফাংশন কল শুরু হওয়ার সাথে সাথে এটি (বা কিছু ধারণকৃত কল) পয়েন্ট-টু অবজেক্টটিকে অন্য ডেটা কাঠামোর দিকে নিয়ে যায় এমন কোনও কিছুর ব্যতিক্রম ছুঁড়ে ফেলা হয় (এবং এই ব্যতিক্রমটি ইতিমধ্যে ফাংশনের অভ্যন্তরে ধরা পড়ে না) ), তারপর মোড 1 ব্যবহার করার সময়, কোনও catch
ধারা ব্যতিক্রমটি পরিচালনা করার আগে স্মার্ট পয়েন্টার দ্বারা উল্লিখিত বস্তুটি ধ্বংস হয়ে যাবে (কারণ ফাংশন প্যারামিটারটি স্ট্যাক আনওয়াইন্ড করার সময় ধ্বংস করা হয়েছিল), তবে মোড 3 ব্যবহার করার সময় নয় তবে পরবর্তীটি দেয় কলারের কাছে এই জাতীয় ক্ষেত্রে (ব্যতিক্রম ধরা পড়ে) অবজেক্টের ডেটা পুনরুদ্ধার করার বিকল্প রয়েছে। নোট করুন যে এখানে মোড 1 এখানে মেমরি ফাঁস হওয়ার কারণ নয় , তবে প্রোগ্রামটির জন্য অপ্রত্যাশিত ডেটা হারাতে পারে, এটি অবাঞ্ছিতও হতে পারে।
স্মার্ট পয়েন্টার ফিরিয়ে দেওয়া: সর্বদা মান অনুসারে
স্মার্ট পয়েন্টারটি ফিরিয়ে দেওয়ার বিষয়ে কোনও শব্দ উপসংহারে , সম্ভবত কলার দ্বারা ব্যবহারের জন্য তৈরি করা কোনও বস্তুর দিকে ইঙ্গিত করে। এই সত্যিই একটি কেস ফাংশন মধ্যে পয়েন্টার ক্ষণস্থায়ী সঙ্গে তুলনীয় নয়, কিন্তু সম্পূর্ণতার আমি জিদ যে এই ক্ষেত্রে চাই সবসময় মান আসতে (এবং ব্যবহার করবেন না std::move
যে return
বিবৃতি)। কেউই কেবল এমন পয়েন্টারের রেফারেন্স পেতে চায় না যে সম্ভবত সিক্সড করা হয়েছে।