অনুলিপি প্রতিলিপি কি?


1997

এই প্রবাদটি কী এবং কখন ব্যবহার করা উচিত? এটি কোন সমস্যার সমাধান করে? সি ++ 11 ব্যবহার করার সময় কি আইডিয়ম পরিবর্তন হয়?

যদিও এটি অনেক জায়গায় উল্লেখ করা হয়েছে, প্রশ্ন ও উত্তর আমাদের কোনও একক নেই "এটি কী" আছে তাই এটি এখানে so এখানে পূর্বে উল্লিখিত জায়গাগুলির আংশিক তালিকা এখানে রয়েছে:



2
জট্টিল, আমি আমার থেকে এই প্রশ্নের লিঙ্ক পদক্ষেপ শব্দার্থবিদ্যা উত্তর
ফ্রেডওভারফ্লো

4
এই আইডিয়ামটির জন্য একটি পূর্ণ-ধারণা বিশিষ্ট ধারণা রাখা ভাল ধারণা, এটি এত সাধারণ যে প্রত্যেকেরই এটি সম্পর্কে জানতে হবে।
ম্যাথিউ এম।

16
সতর্কতা: অনুলিপি / সোয়াপ আইডিয়ামটি ব্যবহারের চেয়ে অনেক বেশি ঘন ঘন ব্যবহৃত হয়। কপির অ্যাসাইনমেন্ট থেকে শক্তিশালী ব্যতিক্রমী সুরক্ষা গ্যারান্টির প্রয়োজন না হলে এটি প্রায়শই সম্পাদনের পক্ষে ক্ষতিকারক। এবং যখন অনুলিপি অ্যাসাইনমেন্টের জন্য দৃ exception় ব্যতিক্রম সুরক্ষা প্রয়োজন তখন এটি একটি দ্রুত কপির অ্যাসাইনমেন্ট অপারেটর ছাড়াও একটি স্বল্প জেনেরিক ফাংশন দ্বারা সহজেই সরবরাহ করা হয়। স্লাইডগুলি.নেট / হাইপ্লেলেবস / হাওয়ার্ড-হিন্যান্ট-accu2014 স্লাইডগুলি 43 - 53 টি দেখুন : সংক্ষিপ্তসার: অনুলিপি / সোয়াপ টুলবক্সে একটি দরকারী সরঞ্জাম। তবে এটি অত্যধিক বিপণন করা হয়েছে এবং পরবর্তীতে প্রায়শই অপব্যবহার করা হয়।
হাওয়ার্ড হিনান্ট

2
@ হাওয়ার্ডহিন্যান্ট: হ্যাঁ, এটি +1 আমি এটি এমন সময়ে লিখেছিলাম যেখানে প্রায় প্রতিটি সি ++ প্রশ্ন ছিল "অনুলিপি করার সময় আমার ক্লাস ক্র্যাশ করতে সহায়তা করে" এবং এটি ছিল আমার প্রতিক্রিয়া। এটি উপযুক্ত যখন আপনি কেবল অনুলিপি / মুভ-শব্দার্থক শব্দগুলি বা অন্য যে কোনও কিছুতে চালিত করতে চান তা চাইলেও এটি সর্বোত্তম নয়। আপনি যদি মনে করেন যে এটি সাহায্য করবে তবে আমার উত্তরের শীর্ষে একটি অস্বীকার অস্বীকার করুন free
GManNickG

উত্তর:


2182

সংক্ষিপ্ত বিবরণ

কেন আমাদের অনুলিপি-অনুলিপি প্রয়োজন?

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

কপি-এবং-swap 'র বাগ্ধারা সমাধান, এবং এইরূপ সূচারূভাবে দুটি জিনিস অর্জনের নিয়োগ অপারেটর সহায়তা: এড়ানো কোড অনুলিপি , এবং একটি প্রদানের শক্তিশালী ব্যতিক্রম গ্যারান্টি

এটা কিভাবে কাজ করে?

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

কপি এবং অদলবদ প্রজ্ঞাটি ব্যবহার করার জন্য, আমাদের তিনটি জিনিস দরকার: একটি ওয়ার্কিং কপি-কনস্ট্রাক্টর, একটি ওয়ার্কিং ডেস্ট্রাক্টর (উভয়ই কোনও মোড়কের ভিত্তি, সুতরাং যাইহোক সম্পূর্ণ হওয়া উচিত) এবং একটি swapফাংশন।

একটি অদলবদল ফাংশন একটি নন-নিক্ষেপ ফাংশন যা কোনও শ্রেণীর দুটি বস্তু, সদস্য হিসাবে সদস্যের অদলবদল করে। আমরা std::swapআমাদের নিজস্ব সরবরাহ না করে ব্যবহার করার জন্য প্রলুব্ধ হতে পারি , তবে এটি অসম্ভব হবে; std::swapকপি-কনস্ট্রাক্টর এবং কপি-এসাইনমেন্ট অপারেটরটিকে তার প্রয়োগের মধ্যে ব্যবহার করে এবং আমরা শেষ পর্যন্ত নিজের দিক থেকে অ্যাসাইনমেন্ট অপারেটরটি সংজ্ঞায়িত করার চেষ্টা করব!

(কেবল swapএটিই নয়, অযোগ্য কলগুলি আমাদের কাস্টম সোয়াপ অপারেটরটি ব্যবহার করবে, আমাদের শ্রেণীর অপ্রয়োজনীয় নির্মাণ এবং ধ্বংসের বিষয়টি বাদ std::swapদেবে would)


একটি গভীর গভীরতা

লক্ষ

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

#include <algorithm> // std::copy
#include <cstddef> // std::size_t

class dumb_array
{
public:
    // (default) constructor
    dumb_array(std::size_t size = 0)
        : mSize(size),
          mArray(mSize ? new int[mSize]() : nullptr)
    {
    }

    // copy-constructor
    dumb_array(const dumb_array& other)
        : mSize(other.mSize),
          mArray(mSize ? new int[mSize] : nullptr),
    {
        // note that this is non-throwing, because of the data
        // types being used; more attention to detail with regards
        // to exceptions must be given in a more general case, however
        std::copy(other.mArray, other.mArray + mSize, mArray);
    }

    // destructor
    ~dumb_array()
    {
        delete [] mArray;
    }

private:
    std::size_t mSize;
    int* mArray;
};

এই শ্রেণিটি প্রায় সাফল্যের সাথে অ্যারে পরিচালনা করে তবে এটি operator=সঠিকভাবে কাজ করা দরকার।

একটি ব্যর্থ সমাধান

একটি নিষ্পাপ বাস্তবায়ন দেখতে কেমন হতে পারে তা এখানে:

// the hard part
dumb_array& operator=(const dumb_array& other)
{
    if (this != &other) // (1)
    {
        // get rid of the old data...
        delete [] mArray; // (2)
        mArray = nullptr; // (2) *(see footnote for rationale)

        // ...and put in the new
        mSize = other.mSize; // (3)
        mArray = mSize ? new int[mSize] : nullptr; // (3)
        std::copy(other.mArray, other.mArray + mSize, mArray); // (3)
    }

    return *this;
}

এবং আমরা বলি আমরা শেষ করেছি; এটি এখন ফাঁস ছাড়াই একটি অ্যারে পরিচালনা করে। যাইহোক, এটি তিনটি সমস্যায় ভুগছে, কোডটিতে ধারাবাহিকভাবে চিহ্নিত (n)

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

  2. দ্বিতীয়টি হ'ল এটি কেবল একটি প্রাথমিক ব্যতিক্রম গ্যারান্টি সরবরাহ করে। যদি new int[mSize]ব্যর্থ হয়, *thisঈষত্ পরিবর্তিত হয়েছে হবে। (যথা, আকারটি ভুল এবং ডেটা চলে গেছে!) দৃ exception় ব্যতিক্রম গ্যারান্টির জন্য এটির অনুরূপ কিছু হওয়া দরকার:

    dumb_array& operator=(const dumb_array& other)
    {
        if (this != &other) // (1)
        {
            // get the new data ready before we replace the old
            std::size_t newSize = other.mSize;
            int* newArray = newSize ? new int[newSize]() : nullptr; // (3)
            std::copy(other.mArray, other.mArray + newSize, newArray); // (3)
    
            // replace the old data (all are non-throwing)
            delete [] mArray;
            mSize = newSize;
            mArray = newArray;
        }
    
        return *this;
    }
    
  3. কোডটি প্রসারিত হয়েছে! যা আমাদের তৃতীয় সমস্যার দিকে নিয়ে যায়: কোড নকল। আমাদের অ্যাসাইনমেন্ট অপারেটর কার্যকরভাবে আমরা ইতিমধ্যে অন্য কোথাও লিখেছি সমস্ত কোড নকল করে, এবং এটি একটি ভয়ানক জিনিস।

আমাদের ক্ষেত্রে, এর মূলটি কেবল দুটি লাইন (বরাদ্দ এবং অনুলিপি), তবে আরও জটিল সংস্থান সহ এই কোডটি ব্লাট বেশ ঝামেলা হতে পারে। আমাদের নিজেদেরকে কখনই পুনরাবৃত্তি করার চেষ্টা করা উচিত।

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

একটি সফল সমাধান

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

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

class dumb_array
{
public:
    // ...

    friend void swap(dumb_array& first, dumb_array& second) // nothrow
    {
        // enable ADL (not necessary in our case, but good practice)
        using std::swap;

        // by swapping the members of two objects,
        // the two objects are effectively swapped
        swap(first.mSize, second.mSize);
        swap(first.mArray, second.mArray);
    }

    // ...
};

( কেন তার ব্যাখ্যা এখানেpublic friend swap ;) এখন আমরা কেবল আমাদের dumb_arrayগুলি বদল করতে পারি না , তবে সাধারণভাবে অদলবদল আরও কার্যকর হতে পারে; এটি সম্পূর্ণ অ্যারে বরাদ্দ এবং অনুলিপি করার পরিবর্তে কেবলমাত্র পয়েন্টার এবং আকারকে অদলবদল করে। কার্যকারিতা এবং দক্ষতার এই বোনাস বাদে, আমরা এখন অনুলিপি-অনুলিপি প্রতিস্থাপন করতে প্রস্তুত।

আরও অ্যাডো ছাড়া আমাদের অ্যাসাইনমেন্ট অপারেটরটি হ'ল:

dumb_array& operator=(dumb_array other) // (1)
{
    swap(*this, other); // (2)

    return *this;
}

এবং এটাই! একটি হতাশ হয়ে পড়ে, তিনটিই সমস্যা একবারে সুন্দরভাবে মোকাবেলা করা হয়েছে।

কেন এটি কাজ করে?

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

dumb_array& operator=(const dumb_array& other)
{
    dumb_array temp(other);
    swap(*this, temp);

    return *this;
}

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

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

ফাংশনে প্রবেশ করার পরে লক্ষ্য করুন যে সমস্ত নতুন ডেটা ইতিমধ্যে বরাদ্দ, অনুলিপি এবং ব্যবহারের জন্য প্রস্তুত। এটি আমাদের নিখরচায় একটি শক্তিশালী ব্যতিক্রমের গ্যারান্টি দেয়: অনুলিপিটি নির্মাণে ব্যর্থ হলে আমরা এমনকি ফাংশনে প্রবেশ করব না এবং তাই এর অবস্থার পরিবর্তন করা সম্ভব নয় *this। (দৃ exception় ব্যতিক্রমী গ্যারান্টির জন্য আমরা ম্যানুয়ালি এর আগে যা করেছি, সংকলকটি এখন আমাদের জন্য করছে; কত দয়ালু))

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

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

এবং এটি হ'ল প্রতিলিপি এবং অনুলিপি।

সি ++ 11 সম্পর্কে কী হবে?

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

ভাগ্যক্রমে আমাদের জন্য, এটি সহজ:

class dumb_array
{
public:
    // ...

    // move constructor
    dumb_array(dumb_array&& other) noexcept ††
        : dumb_array() // initialize via default constructor, C++11 only
    {
        swap(*this, other);
    }

    // ...
};

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

সুতরাং আমরা যা করেছি তা সহজ: ডিফল্ট কনস্ট্রাক্টর (একটি সি ++ 11 বৈশিষ্ট্য) এর মাধ্যমে সূচনা করুন, তারপরে অদলবদল করুন other; আমরা জানি আমাদের ক্লাসের একটি ডিফল্ট নির্মিত উদাহরণ নিরাপদে নির্ধারিত ও ধ্বংস হতে পারে, তাই আমরা জানি otherযে অদলবদল করার পরেও এটি করতে সক্ষম হব।

(মনে রাখবেন যে কয়েকটি সংকলক নির্মাণকারী প্রতিনিধিদের সমর্থন করে না; এই ক্ষেত্রে আমাদের ক্লাসটি ম্যানুয়ালি ডিফল্ট করতে হবে This এটি দুর্ভাগ্যজনক তবে ভাগ্যক্রমে তুচ্ছ কাজ)

কেন যে কাজ করে?

আমাদের ক্লাসে এটিই করা আমাদের একমাত্র পরিবর্তন, তবে এটি কেন কাজ করে? পরামিতিটিকে একটি মান হিসাবে উল্লেখ করার জন্য আমরা যে-গুরুত্বপূর্ণ সিদ্ধান্ত নিয়েছি তা মনে রাখুন, রেফারেন্স নয়:

dumb_array& operator=(dumb_array other); // (1)

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

এবং তাই অনুলিপি-অনুলিপি আইডিয়াম।


পাদটিকা

* আমরা কেন mArrayবাতিল করব? কারণ অপারেটরের যদি আরও কোনও কোড নিক্ষেপ করে তবে এর ডেস্ট্রাক্টর dumb_arrayবলা যেতে পারে; এবং যদি এটি বাতিল না করেই ঘটে থাকে তবে আমরা ইতিমধ্যে মুছে ফেলা মেমরিটি মুছে ফেলার চেষ্টা করি! আমরা এটিকে নালায় সেট করে এড়িয়ে চলি, কেননা নালটি মুছে ফেলা কোনও অপারেশন।

Other অন্যান্য দাবী রয়েছে যে std::swapআমাদের আমাদের ধরণের জন্য বিশেষীকরণ করা উচিত , একটি শ্রেণির swapপাশাপাশি একটি ফ্রি-ফাংশন swapইত্যাদি সরবরাহ করা উচিত But তবে এগুলি সমস্ত অপ্রয়োজনীয়: কোনও সঠিক ব্যবহারের swapঅযোগ্য কলের মাধ্যমে হবে এবং আমাদের কাজটি হবে এডিএল মাধ্যমে পাওয়া । একটি ফাংশন করবে।

Reason কারণটি সহজ: আপনার নিজের কাছে একবার সংস্থান হয়ে গেলে আপনি অদলবদল করতে পারেন এবং / অথবা এটি (সি ++ 11) যে কোনও জায়গায় সরিয়ে নিতে পারেন। এবং প্যারামিটার তালিকায় অনুলিপি তৈরি করে, আপনি সর্বোত্তমকরণ করুন।

Move মুভ কনস্ট্রাক্টরটি সাধারণত হওয়া উচিত noexcept, অন্যথায় কিছু কোড (যেমন: std::vectorপুনরায় আকার দেওয়ার লজিক) অনুলিপি তৈরির ক্ষেত্রেও কোনও পদক্ষেপটি বোধগম্য হবে। অবশ্যই, অভ্যন্তর কোডটি ব্যতিক্রম না ছড়িয়ে দিলে কেবল এটিকেই চিহ্নিত করুন।


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

22
আমি জানি না কেন এখানে অদলবদল পদ্ধতিটিকে বন্ধু হিসাবে ঘোষণা করা হয়?
szx

9
@ এসএসডি: এটি ADL এর মাধ্যমে এটি সন্ধানের অনুমতি দেওয়ার জন্য।
GManNickG

8
@ নিউউইমিপুর্ট: প্রথম বন্ধনী ব্যবহারের সাথে অ্যারে উপাদানগুলি ডিফল্টভাবে আরম্ভ করা হয়। ছাড়া, তারা অবিচ্ছিন্ন করা হয়। অনুলিপি নির্মাণকারী যেহেতু আমরা যেভাবেই মানগুলি ওভাররাইট করে যাব, আমরা আরম্ভ করতে পারি না।
GManNickG

10
@ নিউউইমিপার্টে: আপনার swapযদি ADL এর সময় সন্ধান করা উচিত তবে আপনি যদি সর্বাধিক জেনেরিক কোডটিতে কাজ করতে চান যা আপনি জুড়ে আসেন, যেমন boost::swapএবং অন্যান্য বিভিন্ন অদলবদলের উদাহরণ। অদলবদল C ++ এ একটি জটিল সমস্যা, এবং সাধারণত আমরা সকলেই একমত হয়েছি যে একক পয়েন্ট অ্যাক্সেস সবচেয়ে ভাল (ধারাবাহিকতার জন্য), এবং সাধারণভাবে এটি করার একমাত্র উপায় একটি ফ্রি ফাংশন ( intকোনও অদলবদল থাকতে পারে না, উদাহরণ স্বরূপ). কিছু পটভূমি জন্য আমার প্রশ্ন দেখুন ।
GManNickG

274

অ্যাসাইনমেন্ট, এর অন্তরে, দুটি পদক্ষেপ: বস্তুর পুরানো অবস্থা ছিন্ন করা এবং অন্য কোনও বস্তুর অবস্থার অনুলিপি হিসাবে তার নতুন রাষ্ট্র তৈরি করা

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

তার পরিশোধিত ফর্মে, অ্যাসাইনমেন্ট অপারেটরের (অ-রেফারেন্স) পরামিতি শুরু করে অনুলিপিটি সম্পাদন করে অনুলিপি প্রয়োগ করা হবে:

T& operator=(T tmp)
{
    this->swap(tmp);
    return *this;
}

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

7
std::swap(this_string, that)নো-থ্রো গ্যারান্টি সরবরাহ করে না। এটি শক্তিশালী ব্যতিক্রম সুরক্ষা সরবরাহ করে, তবে নো-থ্রো গ্যারান্টি নয়।
উইলহেমটেল

11
@ উইলহেলমটেল: সি ++ 03 তে সম্ভাব্যভাবে ছুঁড়ে দেওয়া ব্যতিক্রমগুলির কোনও উল্লেখ নেই std::string::swap(যার দ্বারা ডাকা হয় std::swap)। সি ++ 0x সালে std::string::swapহয় noexceptএবং ব্যতিক্রমগুলি নিক্ষেপ না।
জেমস ম্যাকনেলিস

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

2
@ ওহেলহেম্টেল: আমি ভেবেছিলাম যে এটি বদলের বিন্দু: এটি কখনই ছোঁড়ে না এবং এটি সর্বদা হে (1) (হ্যাঁ, আমি জানি, std::array...)
এসবিআই

44

ইতিমধ্যে কিছু ভাল উত্তর আছে। আমি প্রধানত আমার যা মনে করি সেগুলিতে আমি ফোকাস করব - অনুলিপি-অনুলিপি আইডিয়মের সাথে "কনস" এর একটি ব্যাখ্যা ....

অনুলিপি প্রতিলিপি কি?

অদলবদল কার্যকারিতার ক্ষেত্রে অ্যাসাইনমেন্ট অপারেটরটি কার্যকর করার একটি উপায়:

X& operator=(X rhs)
{
    swap(rhs);
    return *this;
}

মূল ধারণাটি হ'ল:

  • কোনও বস্তুকে বরাদ্দের সর্বাধিক ত্রুটি-প্রবণ অংশটি হ'ল নতুন রাষ্ট্রের যে প্রয়োজনীয় সংস্থান অর্জিত হয়েছে তা নিশ্চিত করা (যেমন মেমরি, বর্ণনাকারী)

  • নতুন মূল্যটির একটি অনুলিপি তৈরি করা হলে অবজেক্টের বর্তমান অবস্থার (যেমন ) পরিবর্তনের আগে অধিগ্রহণের চেষ্টা করা যেতে পারে *this, তাই কেন রেফারেন্সের পরিবর্তে মান দ্বারা (যেমন অনুলিপি করা হয়) rhsগৃহীত হয়

  • স্থানীয় অনুলিপি রাজ্যের সোয়াপিং rhsএবং *thisহয় সাধারণত ছাড়া সম্ভাব্য ব্যর্থতা / ব্যতিক্রম অপেক্ষাকৃত সহজ করতে দেওয়া স্থানীয় অনুলিপি পরে কোন বিশেষ অবস্থা দরকার নেই (ঠিক রাষ্ট্র হইয়া চালানোর জন্য বিনাশকারী জন্য, অনেক হচ্ছে একটি বস্তু হিসেবে প্রয়োজন সরানো থেকে> = সি ++ ১১)

এটি কখন ব্যবহার করা উচিত? (এটি কোন সমস্যার সমাধান করে [/ তৈরি] ?)

  • যখন আপনি কোনও অ্যাসাইনমেন্ট দ্বারা আপত্তিহীন আপত্তিটিকে অবিচ্ছিন্ন করতে চান যা একটি ব্যতিক্রম ছুঁড়েছে, ধরে নিই আপনার কাছে দৃ swapstrong় ব্যতিক্রম গ্যারান্টি সহ একটি লিখতে বা লিখতে পারেন , এবং আদর্শভাবে ব্যর্থ হতে পারে না এমন একটি throw.. / †

  • যখন আপনি একটি পরিষ্কার, সহজেই বোঝার সহজ, অনুলিপি অপারেটরটিকে (সহজতর) অনুলিপি নির্মাণকারী, swapএবং ডেস্ট্রাক্টর ফাংশনগুলির ক্ষেত্রে নির্ধারণ করার শক্ত উপায় চান want

    • অনুলিপি করা প্রান্তের মামলাগুলি অনুলিপি-অন-স্বরূপ হিসাবে অনুলিপি করা হয়ে থাকে assign

  • কোনও কার্যকারিতা পেনাল্টি বা অ্যাসাইনমেন্টের সময় অতিরিক্ত অস্থায়ী বস্তু থাকার কারণে মুহূর্তের মধ্যে উচ্চতর সংস্থান ব্যবহার আপনার অ্যাপ্লিকেশনটির জন্য গুরুত্বপূর্ণ নয়। ⁂

swapনিক্ষেপ: সাধারণত পয়েন্টার অনুসারে অবজেক্টগুলি ট্র্যাক করে এমন ডেটা সদস্যদের নির্ভরযোগ্যতার সাথে অদলবদল করা সম্ভব হয় তবে নন-পয়েন্টার ডেটা সদস্যদের যে কোনও থ্রো-ফ্রি অদলবদল নেই, বা যার জন্য অদলবদল প্রয়োগ করা হয় X tmp = lhs; lhs = rhs; rhs = tmp;এবং অনুলিপি-নির্মাণ বা কার্যনির্বাহী ফেলে দিতে পারে, এখনও কিছু ডেটা সদস্যকে অদলবদল করে এবং অন্যদের না রেখে ব্যর্থ হওয়ার সম্ভাবনা রয়েছে। এই সম্ভাব্যতা এমনকি সি ++ 03 এর ক্ষেত্রে প্রযোজ্য std::stringযা জেমসের অন্য উত্তরে মন্তব্য করেছে:

@ উইলহেলমটেল: সি ++ 03 তে, সম্ভবত স্ট্যান্ড :: স্ট্রিং :: অদলবদল (যাকে স্টাডি :: অদলবদল বলা হয়) দ্বারা ছুঁড়ে দেওয়া ব্যতিক্রমগুলির উল্লেখ নেই। সি ++ 0 এক্সে, স্টাড :: স্ট্রিং :: সোয়াপটি অযোগ্য নয় এবং অবশ্যই ব্যতিক্রম ছোঁড়া উচিত নয়। - জেমস ম্যাকনেলিস 22 ডিসেম্বর '10 এ 15:24


‡ কোনও স্বতন্ত্র বস্তু থেকে বরাদ্দকালে অ্যাসাইনমেন্ট অপারেটর বাস্তবায়ন বোধগম্য মনে হয় স্ব-নিয়োগের জন্য সহজেই ব্যর্থ হতে পারে। যদিও এটি অকল্পনীয় মনে হতে পারে যে ক্লায়েন্ট কোড এমনকি স্ব-কার্যনির্বাহনের চেষ্টা করবে, কনটেইনারগুলিতে অ্যালগো অপারেশনের সময় এটি তুলনামূলকভাবে সহজেই ঘটতে পারে, x = f(x);যেখানে কোড fরয়েছে (সম্ভবত কিছু #ifdefশাখাগুলির জন্য) ম্যাক্রো অলা #define f(x) xবা কোনও ফাংশন যার রেফারেন্স ফিরিয়ে দেয় x, বা এমনকি (সম্ভবত অকার্যকর তবে সংক্ষিপ্ত) কোড পছন্দ করুন x = c1 ? x * 2 : c2 ? x / 2 : x;)। উদাহরণ স্বরূপ:

struct X
{
    T* p_;
    size_t size_;
    X& operator=(const X& rhs)
    {
        delete[] p_;  // OUCH!
        p_ = new T[size_ = rhs.size_];
        std::copy(p_, rhs.p_, rhs.p_ + rhs.size_);
    }
    ...
};

স্ব-নিয়োগ তারিখে, উপরের কোড ডিলিট এর x.p_;, পয়েন্ট p_একটি নতুন বরাদ্দ গাদা অঞ্চল এ, তারপর প্রচেষ্টা পড়তে uninitialised ডেটা তাতে (অনির্দিষ্ট আচরণ), যে যদি কিছু খুব অদ্ভুত করে না copyপ্রচেষ্টা যে just- একটি স্ব-ক্ষমতাযুক্ত নিয়োগ বিধ্বস্ত 'টি'!


Copy অনুলিপি-অদলবদ প্রতিচ্ছবি অতিরিক্ত অস্থায়ী (যখন অপারেটরের প্যারামিটারটি অনুলিপি তৈরি করা হয়) ব্যবহারের কারণে অদক্ষতা বা সীমাবদ্ধতার পরিচয় দিতে পারে:

struct Client
{
    IP_Address ip_address_;
    int socket_;
    X(const X& rhs)
      : ip_address_(rhs.ip_address_), socket_(connect(rhs.ip_address_))
    { }
};

এখানে, একটি হাতে লিখিত সম্ভবত এটি একই সার্ভারের সাথে ইতিমধ্যে সংযুক্ত আছে Client::operator=কিনা *thisতা পরীক্ষা করতে পারে rhs(সম্ভবত কার্যকর হলে "রিসেট" কোড প্রেরণ করছে), তবে অনুলিপি-অনুলিপিটি অনুলিপি করে অনুলিপি তৈরি করবে যা সম্ভবত লিখিত হবে একটি স্বতন্ত্র সকেট সংযোগের পরে আসলটি বন্ধ করুন। একটি সাধারণ ইন-প্রসেস ভেরিয়েবল কপির পরিবর্তে কেবল একটি দূরবর্তী নেটওয়ার্ক ইন্টারঅ্যাকশন বোঝাতে পারে তা নয়, এটি সকেট সংস্থান বা সংযোগগুলিতে ক্লায়েন্ট বা সার্ভারের সীমা ছাড়িয়ে চলতে পারে। (অবশ্যই এই শ্রেণীর একটি চমকপ্রদ ইন্টারফেস রয়েছে, তবে এটি অন্য বিষয় ;- পি)।


4
এটি বলেছিল, সকেট সংযোগ কেবল একটি উদাহরণ ছিল - একই নীতিটি কোনও সম্ভাব্য ব্যয়বহুল সূচনা যেমন প্রযোজ্য যেমন হার্ডওয়ার প্রোবিং / ইনিশিয়ালেশন / ক্যালিবিশন সংযোগ ইত্যাদি ..
টনি ডেলরয়

আরও একটি (বৃহত্তর) কন আছে। কারিগরি হিসাবে বর্তমানের চশমা হিসাবে অবজেক্টটির কোনও মুভ-এসাইনমেন্ট অপারেটর থাকবে না! পরে যদি কোনও শ্রেণীর সদস্য হিসাবে ব্যবহার করা হয় তবে নতুন শ্রেণিতে মুভ-কোর্ট স্বয়ংক্রিয়ভাবে উত্পাদিত হবে না! সূত্র: youtu.be/mYrbivnruYw?t=43m14s
user362515

3
এর অনুলিপি কার্যকারী অপারেটরের প্রধান সমস্যা Clientহ'ল নিয়োগ নিষিদ্ধ নয়।
এসবিআই

ক্লায়েন্টের উদাহরণে, ক্লাসটি অনিপিপিযোগ্য করা উচিত।
জন জেড। লি

25

এই উত্তরটি উপরের উত্তরগুলিতে সংযোজন এবং সামান্য পরিবর্তনের মতো।

ভিজ্যুয়াল স্টুডিওর কয়েকটি সংস্করণে (এবং সম্ভবত অন্যান্য সংকলকগুলি) একটি বাগ রয়েছে যা সত্যই বিরক্তিকর এবং এর কোনও অর্থ নেই। সুতরাং আপনি যদি নিজের swapফাংশনটিকে এভাবে ঘোষণা / সংজ্ঞায়িত করেন :

friend void swap(A& first, A& second) {

    std::swap(first.size, second.size);
    std::swap(first.arr, second.arr);

}

... আপনি যখন swapফাংশনটি কল করবেন তখন সংকলকটি আপনাকে চিত্কার করবে :

এখানে চিত্র বর্ণনা লিখুন

এর সাথে একটি friendফাংশন বলা হচ্ছে এবং thisপরামিতি হিসাবে বস্তুটি পাস করার সাথে কিছু করার আছে ।


এর চারপাশের উপায় হ'ল friendকীওয়ার্ড ব্যবহার না করা এবং swapফাংশনটিকে নতুন করে সংজ্ঞায়িত করা :

void swap(A& other) {

    std::swap(size, other.size);
    std::swap(arr, other.arr);

}

এই বার, আপনি কেবল কল করে swapপ্রবেশ করতে পারেন other, এইভাবে সংকলকটি খুশি করুন:

এখানে চিত্র বর্ণনা লিখুন


সর্বোপরি, আপনার 2 টি বস্তু অদলবদল করতে কোনও ফাংশন ব্যবহার করার দরকার নেইfriend । এটি প্যারামিটার হিসাবে একটি অবজেক্ট swapরয়েছে এমন সদস্য ফাংশন করতে ঠিক ততটা otherঅর্থবোধ করে।

আপনার কাছে ইতিমধ্যে thisঅবজেক্টে অ্যাক্সেস রয়েছে , সুতরাং এটি পরামিতি হিসাবে প্রবেশ করা প্রযুক্তিগতভাবে অপ্রয়োজনীয়।


1
@GManNickG dropbox.com/s/o1mitwcpxmawcot/example.cpp dropbox.com/s/jrjrn5dh1zez5vy/Untitled.jpg । এটি একটি সরলীকৃত সংস্করণ। প্রতিবার কোনও প্যারামিটার friendদিয়ে কোনও ফাংশন ডাকলে ত্রুটি দেখা দেয়*this
ওলেকসিয়

1
@ জিএমএনএনজিজি যেমনটি বলেছিলাম, এটি একটি ত্রুটি এবং অন্যান্য লোকের পক্ষে ভাল কাজ করতে পারে। আমি কেবল এমন কিছু লোককে সহায়তা করতে চেয়েছিলাম যাদের আমার মতো সমস্যা হতে পারে। আমি ভিজ্যুয়াল স্টুডিও ২০১২ এক্সপ্রেস এবং ২০১৩ পূর্বরূপ এবং একমাত্র জিনিস যা এটিকে দূরে সরিয়ে দিয়েছিল তা
দিয়েই

8
@GManNickG এটি সমস্ত চিত্র এবং কোড উদাহরণ সহ কোনও মন্তব্যে ফিট করে না। এবং এটি ঠিক আছে যদি লোকেরা নীচে নামায়, আমি নিশ্চিত যে একই বাগটি পেয়েছে এমন কেউ আছেন; এই পোস্টে তথ্য তাদের প্রয়োজন ঠিক হতে পারে।
ওলেকসিয়

14
নোট করুন যে এটি আইডিই কোড হাইলাইট করার (ইন্টেলিজেন্স) কেবলমাত্র একটি বাগ ... এটি কোনও সতর্কতা / ত্রুটি না দিয়ে ঠিক জরিমানা সংকলন করবে।
আম্রো

3
এখানে বনাম বাগ রিপোর্ট করুন দয়া করে যদি আপনি তা ইতিমধ্যেই সম্পন্ন না করে (এবং যদি এটি সংশোধন করা হয়েছে) connect.microsoft.com/VisualStudio
ম্যাট

15

আপনি যখন C ++ 11-স্টাইলের বরাদ্দকারী-সচেতন পাত্রে ডিল করছেন তখন আমি সতর্কতার শব্দটি যুক্ত করতে চাই। অদলবদল এবং অ্যাসাইনমেন্টের মোটামুটি আলাদা শব্দার্থক শব্দ রয়েছে।

সংক্ষিপ্ততার জন্য, আসুন আমরা একটি ধারক বিবেচনা করি std::vector<T, A>, যেখানে Aকিছু রাষ্ট্রীয় বরাদ্দকারী টাইপ রয়েছে এবং আমরা নিম্নলিখিত ফাংশনগুলির তুলনা করব:

void fs(std::vector<T, A> & a, std::vector<T, A> & b)
{ 
    a.swap(b);
    b.clear(); // not important what you do with b
}

void fm(std::vector<T, A> & a, std::vector<T, A> & b)
{
    a = std::move(b);
}

উভয় কর্ম উদ্দেশ্য fsএবং fmদিতে হয় aবলে যে bপ্রাথমিকভাবে ছিল। তবে, একটি গোপন প্রশ্ন রয়েছে: যদি হয় তবে a.get_allocator() != b.get_allocator()? উত্তরটি হল, এটা নির্ভরশীল. লিখি AT = std::allocator_traits<A>

  • যদি AT::propagate_on_container_move_assignmentহয় std::true_type, তবে fmএর বরাদ্দকারীকে aতার মান দিয়ে পুনরায় সাইন করে b.get_allocator(), অন্যথায় এটি তা করে না এবং aএটির মূল বরাদ্দকারী ব্যবহার অবিরত করে। সেক্ষেত্রে ডেটা উপাদানগুলিকে পৃথকভাবে অদলবদল করা দরকার, যেহেতু স্টোরেজ aএবং bএটি উপযুক্ত নয়।

  • যদি AT::propagate_on_container_swapহয় std::true_type, তাহলে fsআশা করা ফ্যাশন উভয় তথ্য এবং allocators বিনিময়সমূহ।

  • যদি AT::propagate_on_container_swapতা হয় std::false_typeতবে আমাদের একটি গতিশীল চেক দরকার।

    • যদি a.get_allocator() == b.get_allocator(), তবে দুটি পাত্রে সামঞ্জস্যপূর্ণ স্টোরেজ ব্যবহার হয় এবং সাধারণ ফ্যাশনে অদলবদল হয়।
    • তবে, যদি a.get_allocator() != b.get_allocator(), প্রোগ্রামটির অপরিবর্তিত আচরণ থাকে (সিএফ। [ধারক। প্রয়োজনীয়তা। জেনারাল / 8]।

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

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