তিনটির বিধি কী?


2145
  • কী একটি বস্তু অনুলিপি মানে?
  • কি কি কপি কন্সট্রাকটর এবং কপি নিয়োগ অপারেটর ?
  • কখন এগুলি আমার নিজের ঘোষণা করার দরকার?
  • আমি কীভাবে আমার জিনিসগুলি অনুলিপি করা থেকে রোধ করতে পারি?

52
দয়া করে পড়া এই পুরো থ্রেড এবং ট্যাগ উইকি আগে আপনি বন্ধ করতে ভোটc++-faq
এসবিআই

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

1
ফ্রেড, সি ++ 1x সম্পর্কিত আপনার উত্তরের একটি আকর্ষণীয় সংযোজন এখানে রয়েছে: স্ট্যাকওভারফ্লো / প্রশ্নগুলি / ৪৮৮২75৫7/২ । আমরা কীভাবে এটি মোকাবেলা করব?
এসবিআই


4
মনে রাখবেন যে, সি ++ 11 হিসাবে, আমি মনে করি এটি পাঁচটি বিধি বা এই জাতীয় কিছুতে আপগ্রেড করা হয়েছে।
প্যাক্সিডিয়াবল

উত্তর:


1793

ভূমিকা

সি ++ মান-শব্দার্থবিজ্ঞান সহ ব্যবহারকারী-সংজ্ঞায়িত প্রকারের ভেরিয়েবলগুলির আচরণ করে । এর অর্থ হ'ল অবজেক্টগুলি বিভিন্ন প্রসঙ্গে সুস্পষ্টভাবে অনুলিপি করা হয়েছে এবং "কোন বিষয়টিকে অনুলিপি করা" বলতে আসলে কী বোঝায় তা আমাদের বোঝা উচিত।

আসুন একটি সহজ উদাহরণ বিবেচনা করুন:

class person
{
    std::string name;
    int age;

public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
};

int main()
{
    person a("Bjarne Stroustrup", 60);
    person b(a);   // What happens here?
    b = a;         // And here?
}

(আপনি যদি name(name), age(age)এই অংশটি নিয়ে বিস্মিত হন তবে এটিকে সদস্য প্রারম্ভিক তালিকা বলা হয় ))

বিশেষ সদস্যের কাজ

কোন personবস্তুর অনুলিপি করার অর্থ কী ? mainফাংশন দুটি স্বতন্ত্র অনুলিপি পরিস্থিতিতে দেখায়। সূচনাটি কপি নির্মাণকারীperson b(a); দ্বারা সম্পাদিত হয় । এটির কাজটি একটি বিদ্যমান অবজেক্টের অবস্থার উপর ভিত্তি করে একটি নতুন জিনিস তৈরি করা। অ্যাসাইনমেন্টটি অনুলিপি অপারেটর দ্বারা সম্পাদিত হয় । এটির কাজটি সাধারণত কিছুটা জটিল হয় কারণ লক্ষ্য বস্তু ইতিমধ্যে কিছু বৈধ অবস্থায় রয়েছে যা মোকাবেলা করা দরকার।b = a

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

[...] অনুলিপি নির্মাণকারী এবং অনুলিপি নিয়োগের অপারেটর, [...] এবং ডেস্ট্রাক্টর বিশেষ সদস্য ফাংশন। [ দ্রষ্টব্য : যখন প্রোগ্রামটি স্পষ্টভাবে তাদের কাছে ঘোষণা না করে তবে বাস্তবায়ন কিছু শ্রেণীর জন্য এই সদস্য ফাংশনগুলি স্পষ্টভাবে ঘোষণা করবে। প্রয়োগগুলি সুস্পষ্টভাবে তাদের সংজ্ঞায়িত করা হয় যদি তারা ব্যবহার করা হয়। [...] শেষ নোট ] [n3126.pdf বিভাগ 12 §1]

ডিফল্টরূপে, কোনও বস্তু অনুলিপি করার অর্থ এর সদস্যদের অনুলিপি করা:

অ-ইউনিয়ন দশম শ্রেণীর জন্য অন্তর্নিহিত সংজ্ঞায়িত অনুলিপি করা অনুলিপিটি তার সাবোবজেক্টগুলির সদস্যপদযুক্ত অনুলিপিটি সম্পাদন করে। [n3126.pdf বিভাগ 12.8 §16]

নন-ইউনিয়ন দশম শ্রেণীর জন্য স্পষ্টভাবে সংজ্ঞায়িত অনুলিপি অপারেটর অপারেটর তার সাবওবজেক্টগুলির সদস্যপত্রে অনুলিপি কার্য সম্পাদন করে। [n3126.pdf বিভাগ 12.8 §30]

অন্তর্নিহিত সংজ্ঞা

এর personমতো দেখতে স্পষ্টভাবে সংজ্ঞায়িত বিশেষ সদস্য ফাংশন :

// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    name = that.name;
    age = that.age;
    return *this;
}

// 3. destructor
~person()
{
}

সদস্য হিসাবে অনুলিপি করা হ'ল আমরা এক্ষেত্রে ঠিক যা চাই: nameএবং ageঅনুলিপি করা হয়, তাই আমরা একটি স্ব-অন্তর্ভুক্ত, স্বাধীন personঅবজেক্ট পাই । সুস্পষ্টভাবে সংজ্ঞায়িত ডেস্ট্রাক্টর সর্বদা খালি থাকে। এই ক্ষেত্রে এটিও ঠিক আছে যেহেতু আমরা কনস্ট্রাক্টরের কোনও সংস্থান অর্জন করি নি। সদস্যদের ধ্বংসকারীদের ডাস্টস্ট্রাক্টর personশেষ হওয়ার পরে সুস্পষ্টভাবে ডাকা হয় :

ডেস্ট্রাক্টরের শরীরে মৃত্যুদন্ড কার্যকর করার পরে এবং দেহের মধ্যে বরাদ্দকৃত কোনও স্বয়ংক্রিয় বস্তু ধ্বংস করার পরে, দশম শ্রেণির একজন ডেস্ট্রাক্টর এক্সের সরাসরি [...] সদস্যদের জন্য ডেস্ট্রাক্টরদের ডাকেন [n3126.pdf 12.4 §6]

রিসোর্স পরিচালনা করা

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

আসুন আমরা সময়মতো প্রাক-মানক সি ++ এ ফিরে যাই। এর মতো কোনও জিনিস ছিল না std::stringএবং প্রোগ্রামাররা পয়েন্টারগুলির প্রেমে ছিল। personবর্গ ভালো তাকিয়ে আছে পারে:

class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};

আজও, লোকেরা এখনও এই স্টাইলে ক্লাস লেখেন এবং সমস্যায় পড়েন: " আমি একজন ব্যক্তিকে ভেক্টরে ঠেলে দিয়েছিলাম এবং এখন আমি স্মৃতিশক্তি স্মৃতিতে ত্রুটি পেয়েছি! " মনে রাখবেন যে ডিফল্টরূপে কোনও জিনিস অনুলিপি করা মানে তার সদস্যদের অনুলিপি করা, তবে সদস্যটিকে nameকেবল অনুলিপি করা একটি পয়েন্টার কপি, না অক্ষর অ্যারে এটি পয়েন্ট! এর বেশ কয়েকটি অপ্রীতিকর প্রভাব রয়েছে:

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

সুস্পষ্ট সংজ্ঞা

সদস্য হিসাবে অনুলিপি করার কাঙ্ক্ষিত প্রভাব নেই, তাই অক্ষরের অ্যারের গভীর অনুলিপিগুলি তৈরি করার জন্য আমাদের অবশ্যই অনুলিপি নির্ধারক এবং অনুলিপি অ্যাসাইনমেন্ট অপারেটরটি সংজ্ঞায়িত করতে হবে:

// 1. copy constructor
person(const person& that)
{
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
}

// 2. copy assignment operator
person& operator=(const person& that)
{
    if (this != &that)
    {
        delete[] name;
        // This is a dangerous point in the flow of execution!
        // We have temporarily invalidated the class invariants,
        // and the next statement might throw an exception,
        // leaving the object in an invalid state :(
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    return *this;
}

প্রারম্ভিককরণ এবং কার্যভারের মধ্যে পার্থক্যটি নোট করুন: nameমেমরি ফাঁস রোধ করতে অ্যাসাইন করার আগে আমাদের অবশ্যই পুরানো অবস্থা ছিন্ন করতে হবে । এছাড়াও, আমাদের ফর্মটির স্ব-নিয়োগের হাত থেকে রক্ষা করতে হবে x = x। যে চেক ছাড়া, delete[] nameধারণকারী অ্যারের মুছতে হবে উৎস , স্ট্রিং কারণ যখন আপনি লিখতে x = x, উভয় this->nameএবং that.nameএকই পয়েন্টার ধারণ করে।

ব্যতিক্রম সুরক্ষা

দুর্ভাগ্যক্রমে, এই সমাধানটি ব্যর্থ হবে যদি new char[...]মেমরি ক্লান্তির কারণে ব্যতিক্রম ছুঁড়ে ফেলে। একটি সম্ভাব্য সমাধান হ'ল একটি স্থানীয় ভেরিয়েবল প্রবর্তন করা এবং বিবৃতিগুলি পুনরায় অর্ডার করা:

// 2. copy assignment operator
person& operator=(const person& that)
{
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
}

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

অনিচ্ছুক সংস্থানসমূহ

কিছু সংস্থানগুলি অনুলিপি করা যায় না বা করা উচিত নয়, যেমন ফাইল হ্যান্ডলস বা মিটেক্সেস। সেক্ষেত্রে অনুলিপি না দিয়ে কেবল অনুলিপি নির্ধারণকারী এবং অনুলিপি কার্যকারী অপারেটরটিকে ঘোষণা করুন private:

private:

    person(const person& that);
    person& operator=(const person& that);

বিকল্পভাবে, আপনি উত্তরাধিকারী হতে পারেন boost::noncopyableবা তাদের মুছে ফেলা হিসাবে ঘোষণা করতে পারেন (সি ++ 11 এবং উপরে):

person(const person& that) = delete;
person& operator=(const person& that) = delete;

তিনজনের নিয়ম

কখনও কখনও আপনাকে এমন একটি শ্রেণি প্রয়োগ করতে হবে যা কোনও সংস্থান পরিচালনা করে। (কখনই একক শ্রেণিতে একাধিক সংস্থান পরিচালনা করবেন না, এটি কেবল ব্যথার দিকে পরিচালিত করবে।) সেক্ষেত্রে তিনটির বিধি মনে রাখবেন :

আপনার যদি নিজেকে স্পষ্টরূপে ডেস্ট্রাক্টর, কপি নির্মাণকারী বা অনুলিপি নিয়োগের অপারেটরটি প্রকাশ করতে হয় তবে আপনার সম্ভবত তিনটিই স্পষ্টভাবে ঘোষণা করা দরকার।

(দুর্ভাগ্যক্রমে, এই "বিধি "টি সি ++ স্ট্যান্ডার্ড বা আমি জেনে নেই এমন কোনও সংকলক প্রয়োগ করে না।)

পাঁচজনের নিয়ম

সি ++ 11 থেকে কোনও অবজেক্টে 2 টি অতিরিক্ত বিশেষ সদস্য ফাংশন রয়েছে: মুভ কনস্ট্রাক্টর এবং মুভ অ্যাসাইনমেন্ট। এই কাজগুলিও কার্যকর করার জন্য পাঁচটি রাজ্যের নিয়ম।

স্বাক্ষর সহ একটি উদাহরণ:

class person
{
    std::string name;
    int age;

public:
    person(const std::string& name, int age);        // Ctor
    person(const person &) = default;                // Copy Ctor
    person(person &&) noexcept = default;            // Move Ctor
    person& operator=(const person &) = default;     // Copy Assignment
    person& operator=(person &&) noexcept = default; // Move Assignment
    ~person() noexcept = default;                    // Dtor
};

শূন্যের নিয়ম

3/5 এর বিধিটিকে 0/3/5 এর বিধি হিসাবেও উল্লেখ করা হয়। নিয়মের শূন্য অংশে বলা হয়েছে যে আপনার শ্রেণি তৈরি করার সময় আপনাকে বিশেষ সদস্যের কোনও কাজ না লেখার অনুমতি দেওয়া হয়েছে।

পরামর্শ

বেশিরভাগ সময়, আপনাকে নিজেরাই কোনও সংস্থান পরিচালনা করার দরকার নেই, কারণ একটি বিদ্যমান শ্রেণি যেমন std::stringইতিমধ্যে এটি আপনার জন্য করে। কেবল std::stringসদস্যকে ব্যবহার করে সাধারণ কোডটি তুলনামূলকভাবে ব্যবহার করে সংযুক্ত এবং ত্রুটি-প্রবণ বিকল্পের সাথে তুলনা করুন char*এবং আপনার বিশ্বাস হওয়া উচিত। যতক্ষণ আপনি কাঁচা পয়েন্টার সদস্যদের থেকে দূরে থাকবেন ততক্ষণ তিনটির নিয়ম আপনার নিজের কোড নিয়ে উদ্বেগের সম্ভাবনা কম।


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

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

4
@ প্রসূন: আমি মনে করি না যে উত্তরটির অর্ধেক অংশ কেটে নেওয়া কোনও নন-সিডাব্লু উত্তরের "সুষ্ঠু সম্পাদনা" হিসাবে দেখা হবে।
এসবিআই

69
আপনি যদি আপনার পোস্টটি সি ++ 11 এর জন্য আপডেট করেন (অর্থাত্ মুভ কনস্ট্রাক্টর / অ্যাসাইনমেন্ট)
আলেকজান্ডার মালাখভ

5
@ সোলিটো ব্যবহারের পরে আপনাকে যে কোনও কিছু প্রকাশ করতে হবে: একত্রীকরণ লকস, ফাইল হ্যান্ডলস, ডাটাবেস সংযোগ, নেটওয়ার্ক সকেট, হিপ মেমরি ...
ফ্রেডওভারফ্লো

509

তিন রুল সি ++ জন্য একটি চলতি নিয়ম, মূলত এই বলে

আপনার ক্লাসের যদি কোন প্রয়োজন হয়

  • একটি অনুলিপি নির্মাণকারী ,
  • একটি এসাইনমেন্ট অপারেটর ,
  • বা ধ্বংসকারী ,

স্পষ্টরূপে সংজ্ঞায়িত করা হয়েছে, তারপরে সম্ভবত তাদের তিনটিই প্রয়োজন ।

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

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

(দ্রষ্টব্য যে সি ++ স্ট্যান্ডার্ডের আসন্ন নতুন সংস্করণ (যা সি ++ 11) সি ++ তে সরানো শব্দার্থক যুক্ত করে, যা সম্ভবত তিনটির বিধি পরিবর্তন করবে However তবে, আমি সি ++ 11 বিভাগ লিখতে খুব কম জানি না তিনটি বিধি সম্পর্কে।)


3
অনুলিপি প্রতিরোধের আরেকটি সমাধান হ'ল এমন কোনও শ্রেণীর উত্তরাধিকারী (ব্যক্তিগতভাবে) যা অনুলিপি করা যায় না (যেমন boost::noncopyable)। এটি আরও পরিষ্কার হতে পারে। আমি মনে করি যে সি ++ 0x এবং "মুছে ফেলা" ফাংশনগুলি এখানে সহায়তা করতে পারে তবে সিনট্যাক্সটি ভুলে গেছে: /
ম্যাথিউ এম।

2
@ ম্যাথিউ: হ্যাঁ, এটিও কার্যকর। তবে যতক্ষণ না noncopyableএই স্টাড লাইবের অংশ হয়, আমি এটিকে তেমন কোনও উন্নতির হিসাবে বিবেচনা করি না। (ওহ, এবং যদি আপনি মুছে ফেলার সিনট্যাক্সটি ভুলে গিয়েছিলেন তবে আপনি যে ইদানিং আমি জানতেন তা ভুলে গিয়েছিলেন :)))
এসবিআই

3
@ দান: এই উত্তরটি দেখুন । যাইহোক, আমি বিদ্ধ কথা বলতে চাই Martinho এর জিরো রুল । আমার কাছে, এটি গত দশকে C ++ এর জন্য থাম্বের সবচেয়ে গুরুত্বপূর্ণ নিয়ম।
এসবিআই

3
মার্টিনহোর শূন্যের বিধিটি এখন আর ভাল (আপাত অ্যাডওয়্যারের টেকওভার ছাড়াই) আর্কাইভ.অর্গে
নাথান কিড

161

বড় তিনটির আইন উপরে উল্লিখিত হিসাবে রয়েছে।

সরল ইংরেজিতে একটি সহজ উদাহরণ, এটি যে ধরণের সমস্যার সমাধান করে:

অ-ডিফল্ট ডেস্ট্রাক্টর

আপনি আপনার কনস্ট্রাক্টরে মেমরি বরাদ্দ করেছেন এবং তাই এটি মুছতে আপনার কোনও ডেস্ট্রাক্টর লিখতে হবে। অন্যথায় আপনি একটি স্মৃতি ফাঁস হতে হবে।

আপনি ভাবতে পারেন যে এটি কাজ হয়ে গেছে।

সমস্যাটি হ'ল, যদি কোনও অনুলিপি আপনার অবজেক্ট থেকে তৈরি করা হয়, তবে অনুলিপিটি মূল অবজেক্টের মতো একই মেমরিটিতে নির্দেশ করবে।

একবার, এর মধ্যে একটির এটির ডেস্ট্রাক্টরের মধ্যে স্মৃতি মুছে ফেলা হয়, অন্যটির অবৈধ মেমরির জন্য একটি পয়েন্টার থাকবে (এটি ড্যাংলিং পয়েন্টার বলা হয়) যখন এটি ব্যবহার করার চেষ্টা করে জিনিস লোমশ হয়ে উঠবে।

অতএব, আপনি একটি অনুলিপি নির্মাণকারীর লেখেন যাতে এটি নতুন বস্তুকে ধ্বংস করার জন্য তাদের নিজস্ব মেমরির টুকরা বরাদ্দ করে।

অ্যাসাইনমেন্ট অপারেটর এবং অনুলিপি নির্মাণকারী

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

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

এটি সমাধানের জন্য আপনি অনুলিপি নির্মাণকারী এবং অ্যাসাইনমেন্ট অপারেটরের নিজের সংস্করণ লিখুন। আপনার সংস্করণগুলি নতুন অবজেক্টগুলিতে পৃথক মেমরি বরাদ্দ করে এবং প্রথম পয়েন্টারটির ঠিকানার পরিবর্তে যে মানগুলিকে নির্দেশ করে সেগুলি জুড়ে অনুলিপি করে।


4
সুতরাং আমরা যদি একটি অনুলিপি নির্মাণকারী ব্যবহার করি তবে অনুলিপিটি তৈরি করা হয়েছে তবে সম্পূর্ণ ভিন্ন মেমরির স্থানে এবং যদি আমরা অনুলিপি নির্মাণকারী ব্যবহার না করি তবে অনুলিপি তৈরি করা হয় তবে এটি একই মেমরির অবস্থানকে নির্দেশ করে। আপনি কি বলতে চাইছেন? সুতরাং অনুলিপি কন্সট্রাক্টর ছাড়াই একটি অনুলিপিটির অর্থ হ'ল নতুন পয়েন্টারটি থাকবে তবে একই মেমরি অবস্থানটি দেখানো হবে তবে আমরা যদি কপির সাথে কনস্ট্রাক্টর ব্যবহারকারীর দ্বারা স্পষ্টভাবে সংজ্ঞায়িত হয়ে থাকি তবে আমাদের কাছে একটি পৃথক মেমোরি অবস্থানের দিকে নির্দেশকারী একটি পৃথক পয়েন্টার থাকবে তবে তথ্য থাকবে having
অবিচ্ছেদ্য

4
দুঃখিত, আমি এই যুগ আগে জবাব দিয়েছি কিন্তু আমার উত্তরটি এখনও এখানে উপস্থিত বলে মনে হচ্ছে না :-( মূলত, হ্যাঁ - আপনি এটি পেয়েছেন :-)
স্টেফান

1
নীতিটি অনুলিপি কার্যকারী অপারেটরে কীভাবে কাজ করে? এই উত্তরটি আরও কার্যকর হবে যদি তিনটি বিধি বিধি 3 য় উল্লেখ করা হয়।
DBedrenko

1
@ ডিবেদ্রেনকো, "আপনি একটি অনুলিপি নির্মাণকারী লিখেছেন যাতে এটি নতুন বস্তুকে তাদের নিজস্ব স্মৃতির টুকরো বরাদ্দ দেয় ..." এটি একই নীতি যা অনুলিপি অপারেটর অপারেটরের ক্ষেত্রে প্রসারিত। আপনি কি মনে করেন না যে আমি তা পরিষ্কার করে দিয়েছি?
স্টিফান

2
@ ডিবেদেনকো, আমি আরও কিছু তথ্য যুক্ত করেছি। এটি কি আরও পরিষ্কার করে দেয়?
স্টিফান

44

মূলত আপনার যদি ডেস্ট্রাক্টর থাকে (ডিফল্ট ডেস্ট্রাক্টর নয়) এর অর্থ হ'ল আপনি যে শ্রেণীরটি নির্ধারণ করেছেন তার কিছু মেমরি বরাদ্দ রয়েছে। ধরুন ক্লাসটি বাইরে কিছু ক্লায়েন্ট কোড বা আপনার দ্বারা ব্যবহৃত হয়েছে।

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

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


36

কোনও বস্তু অনুলিপি করার অর্থ কী? আপনি অবজেক্টগুলি অনুলিপি করার কয়েকটি উপায় রয়েছে - আসুন আপনি যে দুটি প্রকারের বিষয়ে উল্লেখ করছেন তা হ'ল - অনুলিপি এবং অগভীর অনুলিপি।

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

class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;

public changePaint(String newColor)
{
   this.sPrintColor = newColor;
}

public Car(String model, String make, String color) //Constructor
{
   this.sPrintColor = color;
   this.sModel = model;
   this.sMake = make;
}

public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}

public Car(const Car &other) // Copy Constructor
{
   this.sPrintColor = other.sPrintColor;
   this.sModel = other.sModel;
   this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
   if(this != &other)
   {
      this.sPrintColor = other.sPrintColor;
      this.sModel = other.sModel;
      this.sMake = other.sMake;
   }
   return *this;
}

}

একটি গভীর অনুলিপি হ'ল যদি আমরা কোনও বিষয় ঘোষণা করি এবং তারপরে অবজেক্টটির সম্পূর্ণ পৃথক অনুলিপি তৈরি করি ... আমরা মেমরির 2 টি সম্পূর্ণ সেটগুলিতে 2 টি অবজেক্ট দিয়ে শেষ করি।

Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.

এবার আসুন অদ্ভুত কিছু করি। ধরা যাক কার 2 কে প্রোগ্রামিংটি ভুল বা উদ্দেশ্যমূলকভাবে বোঝানো হয়েছে কার 1 এর তৈরির প্রকৃত মেমরিটি ভাগ করে নেওয়া। (এটি করা সাধারণত একটি ভুল এবং ক্লাসে সাধারণত এটি কম্বল হিসাবে এটির অধীনে আলোচনা করা হয় tend) tendান করুন যে আপনি যে কোনও সময় কার 2 সম্পর্কে জিজ্ঞাসা করছেন, আপনি সত্যিই কার 1 এর মেমরি স্পেসে একটি পয়েন্টারটি সমাধান করছেন ... এটি কম-বেশি কোনও অগভীর অনুলিপি হয়।

//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.

 Car car1 = new Car("ford", "mustang", "red"); 
 Car car2 = car1; 
 car2.changePaint("green");//car1 is also now green 
 delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
 the address of where car2 exists and delete the memory...which is also
 the memory associated with your car.*/
 car1.changePaint("red");/*program will likely crash because this area is
 no longer allocated to the program.*/

সুতরাং আপনি যে ভাষায় লিখছেন তা নির্বিশেষে, অনুলিপি করা অবজেক্টগুলির ক্ষেত্রে আপনার অর্থ কী তা সম্পর্কে খুব সাবধানতা অবলম্বন করুন কারণ বেশিরভাগ সময় আপনি গভীর অনুলিপি চান।

কপি কনস্ট্রাক্টর এবং কপি অ্যাসাইনমেন্ট অপারেটর কী? আমি ইতিমধ্যে তাদের উপরে ব্যবহার করেছি। আপনি যখন কোডটি টাইপ করেন তখন অনুলিপি কনস্ট্রাক্টরকে ডেকে আনা হয় Car car2 = car1; প্রয়োজনীয়ভাবে যদি আপনি কোনও ভেরিয়েবল ঘোষণা করেন এবং এটিকে একটি লাইনে অর্পণ করেন, তখনই কপি কনস্ট্রাক্টর বলা হয়। নিয়োগ অপারেটর যখন আপনি একটি সমান sign-- ব্যবহার যা হওয়ার তাই হল car2 = car1;। বিজ্ঞপ্তি car2একই বিবৃতিতে ঘোষণা করা হয় না। এই ক্রিয়াকলাপগুলির জন্য আপনি যে দুটি কোড লিখেছেন তা সম্ভবত খুব একই রকম। আসলে সাধারণ নকশার প্যাটার্নটির আরেকটি ফাংশন রয়েছে যা আপনি একবারে সেট করার জন্য কল করেছিলেন প্রাথমিক কপি / অ্যাসাইনমেন্টটি বৈধ - আপনি যদি আমার লেখা লংহ্যান্ড কোডটি দেখেন তবে ফাংশনগুলি প্রায় অভিন্ন।

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

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


5
প্রশ্নটি সি ++ ট্যাগ করা হয়েছিল। এই সিউডো কোড এক্সপোশনটি সর্বোত্তমভাবে সংজ্ঞায়িত "তিনটি বিধি" সম্পর্কে কিছু স্পষ্ট করতে খুব কম কাজ করে এবং সবচেয়ে খারাপ সময়ে কেবল বিভ্রান্তি ছড়িয়ে দেয়।
se

26

কখন এগুলি আমার নিজের ঘোষণা করার দরকার?

তিনটির বিধি বলে যে আপনি যদি কোনওটি ঘোষণা করেন

  1. কপি নির্মাণকারী
  2. অনুলিপি অপারেটর
  3. বিনাশকারী

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

  • একটি অনুলিপি অপারেশনে রিসোর্স ম্যানেজমেন্ট যা যা করা হয়েছিল সম্ভবত অন্য অনুলিপি অপারেশনে করা দরকার ছিল এবং

  • ক্লাস ডেস্ট্রাক্টর রিসোর্স পরিচালনায় অংশ নেবে (সাধারণত এটি প্রকাশ করে)। পরিচালিত হওয়া ক্লাসিক রিসোর্সটি ছিল মেমরি, এবং এ কারণেই মেমরি পরিচালনা করে এমন সমস্ত স্ট্যান্ডার্ড গ্রন্থাগার ক্লাস (যেমন, এসটিএল ধারকগুলি যা গতিশীল মেমরি পরিচালনা করে) সমস্তগুলি "বিগ থ্রি" ঘোষণা করে: অনুলিপি অপারেশন এবং ধ্বংসকারী উভয়ই।

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

আমি কীভাবে আমার জিনিসগুলি অনুলিপি করা থেকে রোধ করতে পারি?

অনুলিপি নির্মাণকারী এবং অনুলিপি অপারেটরটিকে ব্যক্তিগত অ্যাক্সেস সুনির্দিষ্ট হিসাবে ঘোষণা করুন।

class MemoryBlock
{
public:

//code here

private:
MemoryBlock(const MemoryBlock& other)
{
   cout<<"copy constructor"<<endl;
}

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
 return *this;
}
};

int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

সি ++ 11 এর পরে আপনি অনুলিপি নির্মাণকারী এবং অ্যাসাইনমেন্ট অপারেটর মোছাও ঘোষণা করতে পারেন

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

16

বিদ্যমান উত্তরগুলির মধ্যে অনেকগুলি ইতিমধ্যে কপি নির্মাণকারী, অ্যাসাইনমেন্ট অপারেটর এবং ডেস্ট্রাক্টরকে স্পর্শ করে। তবে, সি ++ 11 পোস্টে, মুভ সেমেন্টিকের ভূমিকা এটি 3 ছাড়িয়ে যেতে পারে।

সম্প্রতি মাইকেল ক্লাইজ এমন একটি বক্তৃতা দিয়েছেন যা এই বিষয়টিকে স্পর্শ করেছে: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class


10

সি ++ এ তিনটির বিধি হ'ল নকশা এবং তিনটি প্রয়োজনীয়তার বিকাশের একটি মৌলিক নীতি যা যদি নিম্নলিখিত সদস্যের কোনও কার্যক্রমে স্পষ্ট সংজ্ঞা থাকে, তবে প্রোগ্রামারটিকে অন্য দুটি সদস্যকে একসাথে কার্যকারিতা সংজ্ঞায়িত করতে হবে। যথা: নিম্নলিখিত তিন সদস্যের ফাংশন অপরিহার্য: ধ্বংসকারী, অনুলিপি নির্মাণকারী, অনুলিপি কার্যকারী অপারেটর।

সি ++ এ কপি কন্সট্রাক্টর একটি বিশেষ নির্মাণকারী। এটি একটি নতুন অবজেক্ট তৈরি করতে ব্যবহৃত হয় যা বিদ্যমান অবজেক্টের অনুলিপিটির সমতুল্য নতুন বস্তু।

অনুলিপি অ্যাসাইনমেন্ট অপারেটর একটি বিশেষ অ্যাসাইনমেন্ট অপারেটর যা সাধারণত একই ধরণের অবজেক্টের অন্যদের কাছে বিদ্যমান অবজেক্টটি নির্দিষ্ট করতে ব্যবহৃত হয়।

দ্রুত উদাহরণ রয়েছে:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

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

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