আমি কীভাবে কোনও কনস্ট্রাক্টর বা কোনও ফাংশনে অনন্য_আপনার যুক্তিটি পাস করব?


400

আমি সি ++ 11 এ শব্দার্থক স্থানান্তর করতে নতুন এবং আমি কীভাবে unique_ptrকনস্ট্রাক্টর বা ফাংশনগুলিতে পরামিতিগুলি পরিচালনা করতে পারি তা খুব ভালভাবে জানি না । এই শ্রেণীর রেফারেন্সিং নিজেকে বিবেচনা করুন:

#include <memory>

class Base
{
  public:

    typedef unique_ptr<Base> UPtr;

    Base(){}
    Base(Base::UPtr n):next(std::move(n)){}

    virtual ~Base(){}

    void setNext(Base::UPtr n)
    {
      next = std::move(n);
    }

  protected :

    Base::UPtr next;

};

এইভাবে কি আমার unique_ptrযুক্তি গ্রহণ করে ফাংশনগুলি লিখতে হবে ?

এবং আমার std::moveকলিং কোডটি ব্যবহার করা দরকার ?

Base::UPtr b1;
Base::UPtr b2(new Base());

b1->setNext(b2); //should I write b1->setNext(std::move(b2)); instead?


1
আপনি খালি পয়েন্টারে b1-> setNext কল করার সাথে সাথে কি এই সেগমেন্টেশন দোষ নয়?
বালকি

উত্তর:


836

এখানে যুক্তি হিসাবে একটি অনন্য পয়েন্টার গ্রহণের সম্ভাব্য উপায়গুলি, পাশাপাশি তাদের সম্পর্কিত অর্থ।

(ক) মূল্য অনুসারে

Base(std::unique_ptr<Base> n)
  : next(std::move(n)) {}

ব্যবহারকারীকে এই কল করার জন্য তাদের অবশ্যই নিম্নলিখিতগুলির একটি করতে হবে:

Base newBase(std::move(nextBase));
Base fromTemp(std::unique_ptr<Base>(new Base(...));

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

এটি নিশ্চিত করা হয়েছে কারণ আমরা মান দ্বারা পরামিতি গ্রহণ করি। std::moveআসলে কিছুই সরানো হয় না ; এটি কেবল অভিনব cast std::move(nextBase)এটির Base&&জন্য একটি আর-মান উল্লেখ রয়েছে nextBase। এটিই তাই করে।

কারণ Base::Base(std::unique_ptr<Base> n)তার যুক্তিটিকে আর-মান উল্লেখের পরিবর্তে মান দ্বারা গ্রহণ করে, C ++ স্বয়ংক্রিয়ভাবে আমাদের জন্য একটি অস্থায়ী তৈরি করবে। এটি এর মাধ্যমে একটি তৈরি std::unique_ptr<Base>করে Base&&যা আমরা ফাংশনটি দিয়ে দিয়েছিলাম std::move(nextBase)। এটি এই অস্থায়ীটির নির্মাণ যা মূলত ফাংশন আর্গুমেন্টের থেকে মানটিকে সরিয়ে দেয় ।nextBasen

(খ) অ-নিরপেক্ষ এল-মান উল্লেখ দ্বারা

Base(std::unique_ptr<Base> &n)
  : next(std::move(n)) {}

এটি একটি প্রকৃত এল-মান (একটি নাম পরিবর্তনশীল) কল করতে হবে। এটিকে অস্থায়ীভাবে বলা যায় না:

Base newBase(std::unique_ptr<Base>(new Base)); //Illegal in this case.

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

Base newBase(nextBase);

nextBaseখালি যে কোনও গ্যারান্টি নেই । এটি খালি থাকতে পারে; এটা নাও পারে। এটি সত্যিই কি Base::Base(std::unique_ptr<Base> &n)করতে চায় তার উপর নির্ভর করে । যে কারণে, এটি ঘটতে চলেছে কেবল ফাংশনের স্বাক্ষর থেকে খুব স্পষ্ট হয় না; আপনাকে বাস্তবায়ন (বা সম্পর্কিত ডকুমেন্টেশন) পড়তে হবে।

যে কারণে, আমি এটি একটি ইন্টারফেস হিসাবে পরামর্শ দেব না।

(গ) কনস্টেন্ট এল-মান রেফারেন্স দ্বারা By

Base(std::unique_ptr<Base> const &n);

আমি কোনও বাস্তবায়ন দেখাব না, কারণ আপনি একটি থেকে সরাতে পারবেন নাconst& । একটি পাস করে const&আপনি বলছেন যে ফাংশনটি Baseপয়েন্টারের মাধ্যমে অ্যাক্সেস করতে পারে তবে এটি এটি কোথাও সঞ্চয় করতে পারে না । এটি এর মালিকানা দাবি করতে পারে না।

এটি দরকারী হতে পারে। আপনার নির্দিষ্ট কেসটির জন্য অগত্যা নয়, তবে কাউকে পয়েন্টার হস্তান্তর করতে সক্ষম হওয়া এবং তারা যে (সি ++ এর নিয়ম ভঙ্গ না করে, ফেলে দেওয়া ছাড়াই const) এর মালিকানা দাবি করতে পারে না তা সর্বদা ভাল । তারা এটি সংরক্ষণ করতে পারে না। তারা এটি অন্যের কাছে প্রেরণ করতে পারে তবে অন্যদেরও একই নিয়ম মেনে চলতে হয়।

(ডি) আর-মান উল্লেখ দ্বারা

Base(std::unique_ptr<Base> &&n)
  : next(std::move(n)) {}

এটি "নন-কনস্ট্যান্ড এল-মান রেফারেন্স" কেসের সাথে কম-বেশি অভিন্ন। পার্থক্য দুটি জিনিস।

  1. আপনি একটি অস্থায়ী পাস করতে পারেন :

    Base newBase(std::unique_ptr<Base>(new Base)); //legal now..
  2. অস্থায়ী আর্গুমেন্টগুলি পাস করার সময় আপনাকে অবশ্যই ব্যবহার করতে হবেstd::move

পরেরটি আসলেই সমস্যা। আপনি যদি এই লাইনটি দেখতে পান:

Base newBase(std::move(nextBase));

আপনার একটি যুক্তিসঙ্গত প্রত্যাশা রয়েছে যে এই লাইনটি শেষ হওয়ার পরে, nextBaseখালি থাকা উচিত। এটি থেকে সরানো উচিত ছিল। সর্বোপরি, আপনি std::moveসেখানে বসে আছেন এবং আপনাকে বলছেন যে আন্দোলন হয়েছে।

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

প্রস্তাবনা

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

কীভাবে অনন্য_পিটার হেরফের করবেন

আপনি কপি করতে পারবেন না unique_ptr। আপনি কেবল এটি স্থানান্তর করতে পারেন। এটি করার সঠিক উপায় হ'ল std::moveস্ট্যান্ডার্ড লাইব্রেরি ফাংশন সহ with

আপনি যদি unique_ptrমান অনুসারে নেন তবে আপনি এটিকে অবাধে সরাতে পারেন। তবে আন্দোলন আসলে ঘটে না বলে std::move। নিম্নলিখিত বিবৃতি নিন:

std::unique_ptr<Base> newPtr(std::move(oldPtr));

এটি সত্যই দুটি বিবৃতি:

std::unique_ptr<Base> &&temporary = std::move(oldPtr);
std::unique_ptr<Base> newPtr(temporary);

(দ্রষ্টব্য: উপরের কোডটি প্রযুক্তিগতভাবে সংকলন করে না, যেহেতু অস্থায়ী আর-মান উল্লেখগুলি আসলে আর-মান হয় না It এটি কেবল ডেমো উদ্দেশ্যেই এখানে রয়েছে)।

temporaryমাত্র একটি r- মান রেফারেন্স oldPtr। এটা তোলে হয় কন্সট্রাকটর এর newPtrযেখানে আন্দোলন ঘটবে। unique_ptrএর মুভ কনস্ট্রাক্টর (এমন কনস্ট্রাক্টর যা নিজের &&কাছে নেয়) হ'ল আসল গতিবিধি।

আপনার যদি কোনও unique_ptrমান থাকে এবং আপনি এটি কোথাও সঞ্চয় করতে চান তবে আপনার অবশ্যই স্টোরেজটি ব্যবহার std::moveকরতে হবে।


5
@ নিকোল: তবে std::moveএটির ফেরতের মানটির নাম নেই। মনে রাখবেন যে নামযুক্ত মূল্যসূচক উল্লেখগুলি হল লভ্য। আইডোন.ও.ভিএলইএম 3
আর মার্টিনহো ফার্নান্দেস

31
আমি মূলত এই উত্তরটির সাথে একমত, তবে কিছু মন্তব্য আছে। (1) আমি মনে করি না কনস্ট লভালুতে রেফারেন্স পাস করার জন্য কোনও বৈধ ব্যবহারের কেস রয়েছে: কলি তার সাথে যা করতে পারে তা কনস্ট (খালি) পয়েন্টারের সাথেও খুব ভাল করতে পারে, বা পয়েন্টারটি নিজেই আরও ভাল [এবং এটি মালিকানাধীন আউট করা হয় তা জানতে তার ব্যবসায়ের কোনটিই unique_ptr; হতে পারে কিছু অন্যান্য কলার একই কার্যকারিতা প্রয়োজন তবে তার shared_ptrপরিবর্তে ধরে রাখছেন] (২) লভ্যালু রেফারেন্সের মাধ্যমে কল দরকারী হতে পারে যদি ফাংশনটি পয়েন্টারটি সংশোধন করে, উদাহরণস্বরূপ, লিঙ্কযুক্ত তালিকাগুলি থেকে (তালিকা-মালিকানাধীন) নোড যুক্ত বা অপসারণ করে।
মার্ক ভ্যান লিউউইন

8
... (3) যদিও আপনার যুক্তি যথাযথ রেফারেন্সটি পাস করার চেয়ে বেশি মূল্য দিয়ে যাওয়ার পক্ষে সমর্থন করে তবে আমি মনে করি যে মানটি সর্বদা unique_ptrমূল্যকে রেফারেন্সের মাধ্যমে মানগুলি পাস করে (উদাহরণস্বরূপ এগুলিতে রূপান্তর করার সময় shared_ptr)। এর পক্ষে যুক্তিটি হতে পারে যে এটি সামান্য বেশি দক্ষ (অস্থায়ী পয়েন্টারগুলিতে সরানো কোনও কাজ করা হয়নি) যখন এটি কলকারীকে ঠিক একই অধিকার দেয় (মাপকাঠিগুলি আবৃত করতে পারে বা ল্যাভেলুগুলি জড়িত থাকতে পারে std::moveতবে নগ্ন লিভালু নয়)।
মার্ক ভ্যান লিউউয়েন

19
শুধু পুনরাবৃত্তি কি মার্ক বলেছেন, মূল্য উদ্ধৃতি Sutter : "unique_ptr & A প্যারামিটার হিসাবে একটি const ব্যবহার করবেন না; ব্যবহার উইজেট পরিবর্তে *"
জন

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

57

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 কল ফলাফলের সাথে প্রাসঙ্গিক প্রনয়ন হিসেবে প্রশ্নের জন্য copyinitialiser তালিকা শুশ্রূষা এর পদক্ষেপ কন্সট্রাকটর মধ্যে 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) এখনও পাইলফ করা যায়নি । সুতরাং ত্রুটি নিক্ষেপ করা হলে মূল স্মার্ট পয়েন্টারটি এখনও মূল তালিকা ধরে রাখে; সেই তালিকাটি হয় স্মার্ট পয়েন্টার ডেস্ট্রাক্টর দ্বারা যথাযথভাবে ধ্বংস হয়ে যাবে, বা পর্যাপ্ত প্রারম্ভিক ধারাটির কারণে যদি বেঁচে থাকতে পারে তবে এটি এখনও মূল তালিকাটি ধারণ করবে।nextnodellcatch

এটি একটি গঠনমূলক উদাহরণ ছিল; এই প্রশ্নের চোখের সামনে কেউ কোনও প্রদত্ত মান সম্বলিত প্রথম নোড অপসারণের আরও ধ্বংসাত্মক উদাহরণ দিতে পারে, যদি কোনও:

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বিবৃতি)। কেউই কেবল এমন পয়েন্টারের রেফারেন্স পেতে চায় না যে সম্ভবত সিক্সড করা হয়েছে।


1
মোড 0 এর জন্য +1 - অনন্য_প্ট্রির পরিবর্তে অন্তর্নিহিত পয়েন্টারটি উত্তীর্ণ করা। সামান্যভাবে বিষয়বস্তু বন্ধ করুন (যেহেতু প্রশ্নটি অনন্য_প্টর পাশ করার বিষয়ে) তবে এটি সহজ এবং সমস্যাগুলি এড়ানো।
মাচতা

" এখানে মোড 1 এখানে মেমরি ফাঁস দেয় না " - এর দ্বারা বোঝা যায় যে মোড 3টি একটি মেমরি ফাঁসের কারণ দেয় যা সত্য নয়। unique_ptrস্থানান্তরিত হয়েছে কি না তা নির্বিশেষে, এটি এখনও যখনই ধ্বংস বা পুনরায় ব্যবহার করা হয় তখনও এটি মানটিকে দুর্দান্তভাবে মুছে ফেলবে।
rustx

@ রুস্টিক্স: আপনি দেখতে পাচ্ছেন না যে আপনি কীভাবে এই বিষয়টি বোঝাতে চেয়েছেন এবং আমি কখনই আপনাকে বোঝাতে চাইনি তা বলার ইচ্ছা করি না। আমি কেবল বোঝাতে চেয়েছিলাম যে অন্য কোথাও ব্যবহারের unique_ptrফলে মেমরি ফুটো প্রতিরোধ হয় (এবং এভাবে একটি অর্থে এটি চুক্তিটি পূর্ণ করে) তবে এখানে (অর্থাত্ মোড 1 ব্যবহার করা) এটি এমন কিছু কারণ হতে পারে (নির্দিষ্ট পরিস্থিতিতে) আরও বেশি ক্ষতিকারক হিসাবে বিবেচিত হতে পারে , অর্থ উপাত্তের ক্ষতি (নির্দেশিত মূল্যের ধ্বংস) যা মোড ৩ ব্যবহার করা এড়ানো যেত
মার্ক ভ্যান লিউউইন

4

হ্যাঁ আপনি যদি unique_ptrকনস্ট্রাক্টরের মানটি নেন তবে আপনাকে তা করতে হবে । স্পষ্টতা একটি সুন্দর জিনিস। যেহেতু unique_ptrঅপ্রয়োজনীয় (প্রাইভেট কপি কর্টর) তাই আপনি যা লিখেছেন তা আপনাকে একটি সংকলক ত্রুটি দেয়।


3

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

এর মূল ধারণাটি ::std::moveহ'ল যে সমস্ত লোকেরা আপনাকে পাস করছে unique_ptrতাদের এই জ্ঞানটি প্রকাশ করতে এটি ব্যবহার করা উচিত যে তারা জানে যে unique_ptrতারা যে পাস করছে তার মালিকানা হারাবে।

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

এর অর্থ আপনার দুটি পদ্ধতির দেখতে এটির মতো হওয়া উচিত:

Base(Base::UPtr &&n) : next(::std::move(n)) {} // Spaces for readability

void setNext(Base::UPtr &&n) { next = ::std::move(n); }

তারপরে পদ্ধতিগুলি ব্যবহার করে লোকেরা এটি করবে:

Base::UPtr objptr{ new Base; }
Base::UPtr objptr2{ new Base; }
Base fred(::std::move(objptr)); // objptr now loses ownership
fred.setNext(::std::move(objptr2)); // objptr2 now loses ownership

আপনি দেখতে পাচ্ছেন ::std::moveযে, পয়েন্টারটি যে স্থানে এটি সবচেয়ে প্রাসঙ্গিক এবং সহায়ক তা সেই জায়গায় মালিকানা হারাতে চলেছে তা প্রকাশ করে। যদি অদৃশ্যভাবে এটি ঘটে থাকে তবে objptrসহজেই আপাত কারণে অকারণে আপনার শ্রেণি ব্যবহার করা লোকদের মালিকানা হারাতে হবে তা খুব বিভ্রান্তিকর ।


2
নামযুক্ত মূল্যসূত্র উল্লেখগুলি হ'ল মূল্য।
আর মার্টিনহো ফার্নান্দিস

আপনি কি নিশ্চিত যে এটি Base fred(::std::move(objptr));এবং না Base::UPtr fred(::std::move(objptr));?
কোডাব্ল্যাঙ্ক 1

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

@ কোডাব্ল্যাঙ্ক 1: হ্যাঁ আমি বুনিয়াদী রেফারেন্স গ্রহণকারী কনস্ট্রাক্টর এবং পদ্ধতিগুলি কীভাবে ব্যবহার করব তা আমি প্রদর্শন করছি।
সর্বজনীন

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

0
Base(Base::UPtr n):next(std::move(n)) {}

হিসাবে অনেক ভাল হওয়া উচিত

Base(Base::UPtr&& n):next(std::forward<Base::UPtr>(n)) {}

এবং

void setNext(Base::UPtr n)

হতে হবে

void setNext(Base::UPtr&& n)

একই শরীরের সাথে।

এবং ... কি evtআছে handle()??


3
সেখানে ব্যবহার করে কোন লাভ নেই std::forward: এখান Base::UPtr&&হয় সবসময় একটি rvalue রেফারেন্স প্রকার, এবং std::moveএকটি rvalue যেমন প্রেরণ করা হয়। এটি ইতিমধ্যে সঠিকভাবে ফরওয়ার্ড করা হয়েছে।
আর মার্টিনহো ফার্নান্দেস

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

@ নিকোলবোলাস: ঠিক বলেছেন। আমি আমার উত্তরটি মুছব কারণ এটি কাজ করার সময় আপনার পর্যবেক্ষণটি একেবারে সঠিক।
সর্বজনীন

0

শীর্ষে ভোট দেওয়া উত্তর। আমি মূল্যবোধের রেফারেন্স দিয়ে যেতে পছন্দ করি।

আমি বুঝতে পারি যে মূল্যের রেফারেন্সটি পাস করার ক্ষেত্রে কী সমস্যা হতে পারে। তবে আসুন এই সমস্যাটিকে দুটি পক্ষে বিভক্ত করুন:

  • কলারের জন্য:

আমি কোড লিখতে হবে Base newBase(std::move(<lvalue>))বা Base newBase(<rvalue>)

  • কলির জন্য:

গ্রন্থাগারের লেখকের গ্যারান্টি দেওয়া উচিত এটি মালিকানার মালিকানা চাইলে এটি অনন্য_প্ট্রকে সদস্যের সূচনা করতে প্রথমে স্থানান্তরিত করে।

এখানেই শেষ.

যদি আপনি মূল্যের রেফারেন্স দিয়ে পাস করেন তবে এটি কেবলমাত্র একটি "চলুন" নির্দেশনা প্রেরণ করবে, কিন্তু যদি মান অনুসারে পাস হয় তবে এটি দুটি।

হ্যাঁ, যদি গ্রন্থাগারের লেখক এ সম্পর্কে বিশেষজ্ঞ না হন তবে তিনি সদস্যকে আরম্ভ করতে অনন্য_প্টরকে না স্থান দিতে পারেন, তবে এটি লেখকের সমস্যা, আপনার নয়। এটি মান বা মূল্যসূচক রেফারেন্স দিয়ে যাই পাস না কেন, আপনার কোড একই!

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

এখন, আপনার প্রশ্নের জন্য। আমি কীভাবে কোনও কনস্ট্রাক্টর বা কোনও ফাংশনে অনন্য_আপনার যুক্তিটি পাস করব?

আপনি কি সেরা পছন্দ জানেন।

http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html

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