বাহ, এখানে পরিষ্কার করার মতো অনেক কিছুই আছে ...
প্রথমত, অনুলিপি অ্যাসাইনমেন্ট বাস্তবায়নের জন্য অনুলিপি এবং অদলবদল সর্বদা সঠিক উপায় নয়। প্রায় অবশ্যই ক্ষেত্রে dumb_array
এটি একটি উপ-অনুকূল সমাধান।
ব্যবহারের কপি করুন এবং সোয়াপ জন্য dumb_array
নীচে স্তর এ পূর্ণ বৈশিষ্ট্য সঙ্গে সবচেয়ে ব্যয়বহুল অপারেশন নির্বাণ একটি ক্লাসিক উদাহরণ। এটি এমন ক্লায়েন্টদের জন্য উপযুক্ত যা পুরো বৈশিষ্ট্যটি চায় এবং পারফরম্যান্সের জরিমানা দিতে রাজি হয়। তারা যা চায় ঠিক তা পায়।
তবে এটি ক্লায়েন্টদের জন্য বিপদজনক যেগুলি সম্পূর্ণ বৈশিষ্ট্যের প্রয়োজন নেই এবং পরিবর্তে সর্বোচ্চ কর্মক্ষমতা খুঁজছেন। তাদের dumb_array
জন্য কেবলমাত্র নতুন সফ্টওয়্যারই তাদের আবার লিখতে হবে কারণ এটি খুব ধীর। অন্যভাবে dumb_array
ডিজাইন করা হয়েছে, এটি উভয় ক্লায়েন্টকে কোনও ক্লায়েন্টের সাথে কোনও আপস না করে সন্তুষ্ট করতে পারে।
উভয় ক্লায়েন্টকে সন্তুষ্ট করার চাবিকাঠি হ'ল নিম্নতম স্তরে দ্রুততম ক্রিয়াকলাপ তৈরি করা এবং তারপরে আরও ব্যয় করে পূর্ণ বৈশিষ্ট্যগুলির জন্য এর উপরে এপিআই যুক্ত করা। অর্থাৎ আপনার দৃ you় ব্যতিক্রম গ্যারান্টি দরকার, জরিমানা, আপনি এটির জন্য অর্থ প্রদান করেন। তোমার দরকার নেই? এখানে একটি দ্রুত সমাধান।
আসুন কংক্রিটটি পান: এখানে দ্রুত, মৌলিক ব্যতিক্রমের গ্যারান্টি হ'ল কপির জন্য অ্যাসাইনমেন্ট অপারেটর dumb_array
:
dumb_array& operator=(const dumb_array& other)
{
if (this != &other)
{
if (mSize != other.mSize)
{
delete [] mArray;
mArray = nullptr;
mArray = other.mSize ? new int[other.mSize] : nullptr;
mSize = other.mSize;
}
std::copy(other.mArray, other.mArray + mSize, mArray);
}
return *this;
}
ব্যাখ্যা:
আধুনিক হার্ডওয়্যারটিতে আপনি যে আরও ব্যয়বহুল জিনিসগুলি করতে পারেন তার মধ্যে একটি হ'ল to গাদা ট্রিপ এড়াতে আপনি যা কিছু করতে পারেন তা হ'ল সময় এবং প্রচেষ্টা ভালভাবে ব্যয় করা। এর গ্রাহকরা dumb_array
প্রায়শই একই আকারের অ্যারে নির্ধারণ করতে চাইতে পারেন। এবং যখন তারা করে, আপনার যা করতে হবে তা হ'ল একটি memcpy
(আওতাভুক্ত std::copy
)। আপনি একই আকারের একটি নতুন অ্যারে বরাদ্দ করতে চান না এবং তারপরে একই আকারের পুরানোটিকে বাতিল করতে চান না!
এখন আপনার ক্লায়েন্টদের জন্য যারা প্রকৃতপক্ষে শক্তিশালী ব্যতিক্রমী সুরক্ষা চান:
template <class C>
C&
strong_assign(C& lhs, C rhs)
{
swap(lhs, rhs);
return lhs;
}
অথবা আপনি যদি সি ++ 11 এ মুভ অ্যাসাইনমেন্টের সুবিধা নিতে চান তবে তা হওয়া উচিত:
template <class C>
C&
strong_assign(C& lhs, C rhs)
{
lhs = std::move(rhs);
return lhs;
}
যদি dumb_array
ক্লায়েন্টের মান গতি হয় তবে তাদের কল করা উচিত operator=
। তাদের যদি শক্তিশালী ব্যতিক্রমী সুরক্ষা প্রয়োজন হয় তবে জেনেরিক অ্যালগরিদম রয়েছে যা তারা কল করতে পারেন যা বিভিন্ন ধরণের অবজেক্টগুলিতে কাজ করবে এবং কেবল একবার প্রয়োগ করা দরকার।
এখন মূল প্রশ্নে ফিরে আসুন (যা এই সময়ে একটি টাইপ-ও রয়েছে):
Class&
Class::operator=(Class&& rhs)
{
if (this == &rhs) // is this check needed?
{
// ...
}
return *this;
}
এটি আসলে একটি বিতর্কিত প্রশ্ন। কেউ কেউ হ্যাঁ বলবে, একেবারে, কেউ বলবে না।
আমার ব্যক্তিগত মতামত না, আপনার এই চেকের দরকার নেই।
নীতি:
যখন কোনও বস্তু কোনও মূল্যের রেফারেন্সের সাথে আবদ্ধ হয় তা দুটি জিনিসের মধ্যে একটি:
- একটি অস্থায়ী.
- কলকারী কোনও জিনিস আপনাকে বিশ্বাস করতে চায় এটি একটি অস্থায়ী।
যদি আপনার কাছে কোনও অবজেক্টের একটি রেফারেন্স থাকে যা সত্যিকারের অস্থায়ী হয় তবে সংজ্ঞা অনুসারে আপনার সেই অবজেক্টের একটি অনন্য রেফারেন্স রয়েছে। এটি সম্ভবত আপনার পুরো প্রোগ্রামের অন্য কোথাও উল্লেখ করা যায় না। অর্থাৎ this == &temporary
সম্ভব নয় ।
এখন যদি আপনার ক্লায়েন্ট আপনার কাছে মিথ্যা কথা বলে এবং প্রতিশ্রুতি দেয় যে আপনি যখন নন তখন আপনি একটি অস্থায়ী হয়ে যাচ্ছেন, তবে আপনার যত্ন নেওয়ার দরকার নেই তা নিশ্চিত হওয়া ক্লায়েন্টের দায়িত্ব। আপনি যদি সত্যিই যত্নবান হতে চান তবে আমি বিশ্বাস করি যে এটি আরও ভাল বাস্তবায়ন হবে:
Class&
Class::operator=(Class&& other)
{
assert(this != &other);
// ...
return *this;
}
অর্থাত আপনি যদি হয় একটি স্ব রেফারেন্স পাস, এই ক্লায়েন্ট যে সংশোধন করা উচিত অংশ একটি বাগ।
সম্পূর্ণতার জন্য, এখানে একটি স্থানান্তর নিয়োগের অপারেটর dumb_array
:
dumb_array& operator=(dumb_array&& other)
{
assert(this != &other);
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
সরানো অ্যাসাইনমেন্টের সাধারণ ব্যবহারের ক্ষেত্রে, *this
একটি স্থানান্তরিত অবজেক্ট হবে এবং তাই delete [] mArray;
কোনও অপ-অফ হওয়া উচিত। এটি সমালোচনামূলক যে বাস্তবায়নগুলি নাল্প্টারের উপর যত তাড়াতাড়ি সম্ভব মুছুন।
সতর্কীকরণ:
কিছু তর্ক করবে যে swap(x, x)
এটি একটি ভাল ধারণা, বা কেবল একটি প্রয়োজনীয় মন্দ। এবং এটি, যদি অদলবদল ডিফল্ট অদলবদলে যায় তবে এটি একটি স্ব-মুভ-অ্যাসাইনমেন্টের কারণ হতে পারে।
আমি একমত না যে swap(x, x)
এটি কখনও ভাল ধারণা। যদি আমার নিজের কোডে পাওয়া যায় তবে আমি এটিকে একটি পারফরম্যান্স বাগ হিসাবে বিবেচনা করব এবং এটিকে ঠিক করব। তবে আপনি যদি এটির অনুমতি দিতে চান তবে বুঝতে পারেন যে swap(x, x)
কেবলমাত্র স্থানান্তরিত মানের উপর স্ব-মুভ-অ্যাসিগনেমনেট করে। এবং আমাদের dumb_array
উদাহরণে এটি পুরোপুরি নিরীহ হবে যদি আমরা কেবল দাবী বাদ দিই বা এটিকে সরিয়ে নেওয়া ক্ষেত্রে সীমাবদ্ধ রাখি:
dumb_array& operator=(dumb_array&& other)
{
assert(this != &other || mSize == 0);
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
যদি আপনি দুটি স্থানান্তরিত (খালি) dumb_array
এর স্ব-নিয়োগ দিয়ে থাকেন , তবে আপনি আপনার প্রোগ্রামে অকেজো নির্দেশাবলী সন্নিবেশ করানো বাদ দিয়ে ভুল কিছু করবেন না। এই একই পর্যবেক্ষণ বৃহত সংখ্যা অবজেক্টের জন্য তৈরি করা যেতে পারে।
<
হালনাগাদ>
আমি এই সমস্যাটি আরও কিছু চিন্তা দিয়েছি, এবং আমার অবস্থান কিছুটা পরিবর্তন করেছি। আমি এখন বিশ্বাস করি যে অ্যাসাইনমেন্টটি স্ব-অ্যাসাইনমেন্টের ক্ষেত্রে সহনশীল হওয়া উচিত, তবে অনুলিপি অ্যাসাইনমেন্ট এবং মুভ অ্যাসাইনমেন্টের পোস্টের শর্তগুলি আলাদা:
অনুলিপি নিয়োগের জন্য:
x = y;
একটির একটি পোস্ট-শর্ত y
থাকা উচিত যে এর মান পরিবর্তন করা উচিত নয়। যখন &x == &y
এই postcondition অনুবাদ তারপর: স্ব কপি নিয়োগ মান কোন প্রভাব থাকা উচিত x
।
স্থানান্তরের জন্য:
x = std::move(y);
একটির একটি পোস্ট-শর্ত থাকতে হবে যা y
একটি বৈধ কিন্তু অনির্ধারিত রাজ্য আছে। যখন &x == &y
তারপর এই postcondition অনুবাদ: x
একটি বৈধ কিন্তু অনির্দিষ্ট রাষ্ট্র হয়েছে। অর্থাত্ স্ব-মুভ অ্যাসাইনমেন্টটি কোনও অপ-অফ হতে হবে না। তবে এটি ক্রাশ হওয়া উচিত নয়। এই পোস্ট-শর্তটি swap(x, x)
কেবল কাজ করার অনুমতি দেওয়ার সাথে সামঞ্জস্যপূর্ণ :
template <class T>
void
swap(T& x, T& y)
{
// assume &x == &y
T tmp(std::move(x));
// x and y now have a valid but unspecified state
x = std::move(y);
// x and y still have a valid but unspecified state
y = std::move(tmp);
// x and y have the value of tmp, which is the value they had on entry
}
উপরের কাজগুলি যতক্ষণ x = std::move(x)
না ক্রাশ হয়। এটি x
কোনও বৈধ তবে অনির্ধারিত অবস্থায় ছেড়ে দিতে পারে ।
এটি dumb_array
অর্জনের জন্য আমি মুভ অ্যাসাইনমেন্ট অপারেটরটিকে প্রোগ্রাম করার জন্য তিনটি উপায় দেখছি :
dumb_array& operator=(dumb_array&& other)
{
delete [] mArray;
// set *this to a valid state before continuing
mSize = 0;
mArray = nullptr;
// *this is now in a valid state, continue with move assignment
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
return *this;
}
উপরোক্ত প্রয়োগটি স্ব-কার্যনির্বাহিতা সহ্য করে, *this
এবং other
স্ব-পদক্ষেপের অ্যাসাইনমেন্টের পরে শূন্য আকারের অ্যারে হিসাবে শেষ হয়, এর আসল মান যাই হোক না কেন *this
। ্রফ.
dumb_array& operator=(dumb_array&& other)
{
if (this != &other)
{
delete [] mArray;
mSize = other.mSize;
mArray = other.mArray;
other.mSize = 0;
other.mArray = nullptr;
}
return *this;
}
উপরোক্ত প্রয়োগটি কপিরাইট অ্যাসাইনমেন্ট অপারেটরকে যেমন কোনও অনি-অফ করে তোলে সেভাবে স্ব-কার্য নিয়োগকে সহ্য করে। এটিও ঠিক আছে।
dumb_array& operator=(dumb_array&& other)
{
swap(other);
return *this;
}
উপরোক্তগুলি কেবল তখনই ঠিক আছে যদি dumb_array
"তাত্ক্ষণিকভাবে" ধ্বংস হওয়া উচিত এমন সংস্থান না রাখে। উদাহরণস্বরূপ যদি একমাত্র উত্স মেমরি হয় তবে উপরেরটি ঠিক আছে। যদি dumb_array
সম্ভবত মুটেক্স লক্স বা ফাইলগুলির উন্মুক্ত অবস্থা ধরে রাখতে পারে তবে ক্লায়েন্ট যুক্তিযুক্তভাবে মুভি অ্যাসাইনমেন্টের এলএইচএসে থাকা এই সংস্থানগুলি অবিলম্বে প্রকাশের আশা করতে পারে এবং তাই এই বাস্তবায়ন সমস্যাযুক্ত হতে পারে।
প্রথমটির দাম দুটি অতিরিক্ত স্টোর। দ্বিতীয়টির দাম একটি পরীক্ষা-ও-শাখা। দুটোই কাজ করে। উভয়ই সি ++ 11 স্ট্যান্ডার্ডে টেবিল 22 মুভআসাইনেজেবল প্রয়োজনীয়তার সমস্ত প্রয়োজনীয়তা পূরণ করে। তৃতীয়টি অ-স্মৃতি-সংস্থান-উদ্বেগের মডুলগুলিতেও কাজ করে।
তিনটি বাস্তবায়নের ক্ষেত্রে হার্ডওয়্যারের উপর নির্ভর করে বিভিন্ন ব্যয় হতে পারে: একটি শাখা কত ব্যয়বহুল? প্রচুর রেজিস্ট্রার আছে নাকি খুব কম?
গ্রহণযোগ্যতাটি হ'ল স্ব-মুভ-অ্যাসাইনমেন্ট, স্ব-অনুলিপি-অ্যাসাইনমেন্টের বিপরীতে, বর্তমান মান সংরক্ষণ করতে হবে না।
<
/হালনাগাদ>
লাক ড্যান্টনের মন্তব্য দ্বারা অনুপ্রাণিত হয়ে একটি চূড়ান্ত (আশাবাদী) সম্পাদনা:
যদি আপনি এমন একটি উচ্চ স্তরের শ্রেণি লিখছেন যা সরাসরি মেমরি পরিচালনা করে না (তবে এর বেসগুলি বা সদস্যগুলি থাকতে পারে) তবে সরানো কার্যনির্বাহনের সর্বোত্তম বাস্তবায়ন প্রায়শই:
Class& operator=(Class&&) = default;
এটি প্রতিটি বেস এবং প্রতিটি সদস্যকে পালাক্রমে বরাদ্দ করবে এবং একটি this != &other
চেক অন্তর্ভুক্ত করবে না । কোনও বেসরকারীকে আপনার ঘাঁটি এবং সদস্যদের মধ্যে রক্ষণাবেক্ষণের প্রয়োজন নেই বলে ধরে নিলে এটি আপনাকে সর্বোচ্চ পারফরম্যান্স এবং বেসিক ব্যতিক্রমী সুরক্ষা দেবে। আপনার ক্লায়েন্টদের শক্তিশালী ব্যতিক্রম সুরক্ষার দাবিতে তাদের দিকে নির্দেশ করুন strong_assign
।