সাধারণ লোকেদের ওভারলোড করার জন্য
ওভারলোডিং অপারেটরগুলির বেশিরভাগ কাজ হ'ল বয়লার-প্লেট কোড। এটি আশ্চর্যের কিছু নয় যেহেতু অপারেটরগুলি নিছক সিনট্যাকটিক চিনি, তাই তাদের আসল কাজটি সরল ফাংশন দ্বারা (এবং প্রায়শই ফরোয়ার্ড করা হয়) করা যেতে পারে। তবে এটি গুরুত্বপূর্ণ যে আপনি এই বয়লার-প্লেট কোডটি সঠিকভাবে পান। আপনি যদি ব্যর্থ হন তবে হয় আপনার অপারেটরের কোডটি সংকলন করবে না বা আপনার ব্যবহারকারীর কোড সংকলন করবে না বা আপনার ব্যবহারকারীর কোডটি আশ্চর্যরকম আচরণ করবে।
নিয়োগ অপারেটর
অ্যাসাইনমেন্ট সম্পর্কে অনেক কিছু বলা দরকার। যাইহোক, এর বেশিরভাগই ইতিমধ্যে GMan এর বিখ্যাত কপি-অ্যান্ড-অদলবদ FAQ এ বলা হয়েছে , সুতরাং আমি এখানে বেশিরভাগটি এড়িয়ে যাব, কেবলমাত্র রেফারেন্সের জন্য নিখুঁত অ্যাসাইনমেন্ট অপারেটরকে তালিকাভুক্ত করব:
X& X::operator=(X rhs)
{
swap(rhs);
return *this;
}
বিটশিফ্ট অপারেটর (I / O স্ট্রিমের জন্য ব্যবহৃত)
বিটি শিফট অপারেটর <<
এবং >>
, যদিও তারা সি থেকে উত্তরাধিকার সূত্রে প্রাপ্ত বিট-ম্যানিপুলেশন ফাংশনগুলির জন্য হার্ডওয়্যার ইন্টারফেসিংয়ে ব্যবহৃত হয়, বেশিরভাগ অ্যাপ্লিকেশনগুলিতে ওভারলোডেড স্ট্রিম ইনপুট এবং আউটপুট অপারেটর হিসাবে আরও প্রচলিত হয়ে উঠেছে। বিট-ম্যানিপুলেশন অপারেটর হিসাবে ওভারলোডিংয়ের জন্য গাইডের জন্য বাইনারি অ্যারিমেটিক অপারেটরগুলির নীচের অংশটি দেখুন। আপনার নিজস্ব কাস্টম ফর্ম্যাটটি প্রয়োগ করার জন্য এবং যুক্তি পার্সিংয়ের জন্য যখন আপনার বস্তুটি আইস্ট্রিমেজ ব্যবহার করা হয়, চালিয়ে যান।
সর্বাধিক সাধারণ ওভারলোডেড অপারেটরগুলির মধ্যে স্ট্রিম অপারেটরগুলি বাইনারি ইনফিক্স অপারেটর যার জন্য সিনট্যাক্স তাদের সদস্য বা সদস্য নয় কিনা সে সম্পর্কে কোনও বিধিনিষেধ নির্দিষ্ট করে না। যেহেতু তারা তাদের বাম যুক্তি পরিবর্তন করে (তারা স্ট্রিমের অবস্থার পরিবর্তন করে) তাই তাদের থাম্বের নিয়ম অনুসারে তাদের বাম অপারেন্ডের ধরণের সদস্য হিসাবে প্রয়োগ করা উচিত। তবে, তাদের বাম অপারেশনগুলি স্ট্যান্ডার্ড লাইব্রেরি থেকে স্ট্রিম এবং স্ট্যান্ডার্ড লাইব্রেরি দ্বারা নির্ধারিত বেশিরভাগ স্ট্রিম আউটপুট এবং ইনপুট অপারেটরগুলি প্রকৃতপক্ষে স্ট্রিম শ্রেণির সদস্য হিসাবে সংজ্ঞায়িত করা হয়, যখন আপনি নিজের ধরণের আউটপুট এবং ইনপুট ক্রিয়াকলাপ প্রয়োগ করেন, আপনি স্ট্যান্ডার্ড লাইব্রেরির স্ট্রিমের ধরণগুলি পরিবর্তন করতে পারে না। এজন্য আপনাকে এই অপারেটরগুলি আপনাকে নিজের ধরণের অ-সদস্য ফাংশন হিসাবে প্রয়োগ করতে হবে। দুটির আধ্যাত্মিক রূপগুলি হ'ল:
std::ostream& operator<<(std::ostream& os, const T& obj)
{
// write obj to stream
return os;
}
std::istream& operator>>(std::istream& is, T& obj)
{
// read obj from stream
if( /* no valid object of T found in stream */ )
is.setstate(std::ios::failbit);
return is;
}
বাস্তবায়ন করার সময় operator>>
, ম্যানুয়ালি স্ট্রিমের স্থিতি সেট করা কেবল তখনই প্রয়োজনীয় যখন পড়া নিজেই সফল হয়েছিল, তবে ফলাফলটি কী প্রত্যাশা করা হবে তা নয়।
ফাংশন কল অপারেটর
ফাংশন কল অপারেটর, ফাংশন অবজেক্ট তৈরি করতে ব্যবহৃত হয়, এটি ফান্টেক্টর হিসাবে পরিচিত, অবশ্যই তাকে সদস্য ফাংশন হিসাবে সংজ্ঞায়িত করা উচিত , সুতরাং এটিতে সর্বদা this
সদস্য ফাংশনগুলির অন্তর্নিহিত যুক্তি থাকে। এগুলি ছাড়া শূন্য সহ যে কোনও অতিরিক্ত আর্গুমেন্ট নেওয়ার জন্য এটি ওভারলোড হতে পারে।
এখানে সিনট্যাক্সের একটি উদাহরণ রয়েছে:
class foo {
public:
// Overloaded call operator
int operator()(const std::string& y) {
// ...
}
};
ব্যবহার:
foo f;
int a = f("hello");
পুরো সি ++ স্ট্যান্ডার্ড লাইব্রেরি জুড়ে, ফাংশন অবজেক্টগুলি সর্বদা অনুলিপি করা হয়। আপনার নিজের ফাংশন অবজেক্টগুলি অনুলিপি করার জন্য সস্তা হওয়া উচিত। যদি কোনও ফাংশন অবজেক্টকে একেবারে অনুলিপিযুক্ত ডেটা ব্যবহারের প্রয়োজন হয় তবে সেই ডেটা অন্য কোথাও সঞ্চয় করা এবং ফাংশন অবজেক্টটি এতে উল্লেখ করা ভাল।
তুলনা অপারেটর
বাইনারি ইনফিক্স তুলনা অপারেটরগুলি, থাম্বের নিয়ম অনুসারে, সদস্যবিহীন ফাংশন 1 হিসাবে প্রয়োগ করা উচিত । অ্যানারি উপসর্গের অবহেলা !
(একই নিয়ম অনুসারে) সদস্য ফাংশন হিসাবে প্রয়োগ করা উচিত। (তবে এটি ওভারলোড করা সাধারণত ভাল ধারণা নয়))
স্ট্যান্ডার্ড লাইব্রেরির অ্যালগোরিদম (যেমন std::sort()
) এবং প্রকারগুলি (যেমন std::map
) সবসময় কেবল operator<
উপস্থিত থাকার প্রত্যাশা করবে expect তবে, আপনার ধরণের ব্যবহারকারীরা অন্যান্য অপারেটরদের উপস্থিত থাকারও প্রত্যাশা করবেন , সুতরাং আপনি যদি সংজ্ঞায়িত করেন তবে operator<
অপারেটর ওভারলোডিংয়ের তৃতীয় মৌলিক নিয়মটি অনুসরণ করা এবং অন্যান্য সমস্ত বুলিয়ান তুলনা অপারেটরগুলি সংজ্ঞায়িত করতে ভুলবেন না। এগুলি বাস্তবায়নের প্রচলিত উপায় হ'ল:
inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}
এখানে লক্ষণীয় গুরুত্বপূর্ণ বিষয় হ'ল এই অপারেটরগুলির মধ্যে মাত্র দুটি প্রকৃতপক্ষে কিছু করে, অন্যরা তাদের যুক্তিগুলি প্রকৃত কাজটি করার জন্য এই দুজনের কোনওটির কাছেই প্রেরণ করছে।
অবশিষ্ট বাইনারি বুলিয়ান অপারেটরগুলি ( ||
, &&
) ওভারলোড করার বাক্য গঠন তুলনা অপারেটরগুলির বিধি অনুসরণ করে। তবে, আপনি এই 2 এর জন্য যুক্তিসঙ্গত ব্যবহারের কেসটি খুঁজে পাবেন এমনটি খুব কমই ।
1 থাম্বের সমস্ত নিয়মের মতো, কখনও কখনও এটিও ভাঙার কারণ থাকতে পারে। যদি তা হয় তবে ভুলে যাবেন না যে বাইনারি তুলনা অপারেটরগুলির বাম-হাতের ক্রিয়াকলাপ, যা সদস্য ফাংশনগুলির জন্য হবে তাও হওয়া *this
দরকার const
। সুতরাং কোনও সদস্য কার্য হিসাবে প্রয়োগ করা তুলনা অপারেটরের এই স্বাক্ষর থাকতে হবে:
bool operator<(const X& rhs) const { /* do actual comparison with *this */ }
( const
শেষে নোট করুন ।)
2 এটি লক্ষ করা উচিত যে শর্টকাট শব্দার্থবিজ্ঞানের অন্তর্নির্মিত সংস্করণ ||
এবং &&
ব্যবহার। ব্যবহারকারী সংজ্ঞায়িত ব্যক্তিদের (কারণ তারা পদ্ধতি কলগুলির জন্য সিনট্যাকটিক চিনি) শর্টকাট শব্দার্থবিজ্ঞান ব্যবহার করে না। ব্যবহারকারীরা অপারেটরদের শর্টকাট শব্দার্থবিজ্ঞানের প্রত্যাশা করবেন, এবং তাদের কোড এটির উপর নির্ভর করতে পারে, সুতরাং এগুলি সংজ্ঞায়িত করার জন্য এটি অত্যন্ত পরামর্শ দেওয়া হয় না।
পাটিগণিত অপারেটর
অ্যানারি গণিত অপারেটর
অ্যানারি ইনক্রিমেন্ট এবং হ্রাস অপারেটরগুলি উপসর্গ এবং পোস্টফিক্স উভয় স্বাদে আসে। অন্যটির থেকে একজনকে বলতে, পোস্টফিক্সের রূপগুলি একটি অতিরিক্ত ডামি ইন আর্গুমেন্ট নেয়। যদি আপনি বৃদ্ধি বা হ্রাস ওভারলোড করেন তবে সর্বদা উপসর্গ এবং পোস্টফিক্স সংস্করণ উভয়ই প্রয়োগ করতে ভুলবেন না। এখানে বর্ধিতকরণের আধ্যাত্মিক প্রয়োগ রয়েছে, হ্রাস একই নিয়মগুলি অনুসরণ করে:
class X {
X& operator++()
{
// do actual increment
return *this;
}
X operator++(int)
{
X tmp(*this);
operator++();
return tmp;
}
};
নোট করুন যে পোস্টফিক্স রূপটি উপসর্গের ক্ষেত্রে প্রয়োগ করা হয়েছে। এছাড়াও নোট করুন যে পোস্টফিক্স একটি অতিরিক্ত অনুলিপি করে। 2
ওভারলোডিং অ্যানারি মাইনাস এবং প্লাস খুব সাধারণ নয় এবং সম্ভবত সেরা এড়ানো হবে। যদি প্রয়োজন হয় তবে তাদের সম্ভবত সদস্য ফাংশন হিসাবে ওভারলোড করা উচিত।
2 এছাড়াও নোট করুন যে পোস্টফিক্স বৈকল্পিক আরও বেশি কাজ করে এবং তাই উপসর্গের বৈকল্পিকের চেয়ে কম দক্ষ। পোস্টফিক্স ইনক্রিমেন্টের চেয়ে সাধারণত প্রিফিক্স ইনক্রিমেন্ট পছন্দ করা এটি একটি ভাল কারণ। সংকলকরা সাধারণত অন্তর্নির্মিত প্রকারের জন্য পোস্টফিক্স ইনক্রিমেন্টের অতিরিক্ত কাজটি অপ্টিমাইজ করতে পারে তবে তারা ব্যবহারকারী-সংজ্ঞায়িত প্রকারের জন্য একই কাজ করতে সক্ষম না হতে পারে (যা নিরীহভাবে তালিকার পুনরুক্তিকারীর মতো দেখার মতো কিছু হতে পারে)। একবার আপনি অভ্যস্ত i++
হয়ে যাওয়ার পরে কোনও বিল্ট-ইন টাইপ না হওয়ার ++i
পরিবর্তে এটি মনে রাখা খুব শক্ত হয়ে যায় i
(কোনও টাইপ পরিবর্তন করার সময় আপনার কোডটি পরিবর্তন করতে হবে), তাই সর্বদা অভ্যাস করা ভাল always প্রিফিক্স ইনক্রিমেন্ট ব্যবহার করা, যদি না পোস্টফিক্সের স্পষ্টভাবে প্রয়োজন হয়।
বাইনারি গাণিতিক অপারেটর
বাইনারি গাণিতিক অপারেটরদের জন্য ভুলবেন না তৃতীয় মৌলিক নিয়ম অপারেটর ওভারলোডিং মান্য করা: আপনি প্রদান তাহলে +
, এছাড়াও প্রদান +=
, যদি আপনি প্রদান -
, না ফেরত না -=
, ইত্যাদি অ্যান্ড্রু Koenig প্রথম পালন করা যে যৌগ নিয়োগ হয়েছে বলা হয় অপারেটরগুলি তাদের অ-যৌগিক অংশগুলির জন্য বেস হিসাবে ব্যবহার করা যেতে পারে। অর্থাৎ অপারেটর হয় +
পরিপ্রেক্ষিতে বাস্তবায়িত হয় +=
, -
পদ বাস্তবায়িত হয় -=
ইত্যাদি
আমাদের থাম্বের নিয়ম অনুসারে, +
এবং এর সহযোগীদের সদস্যবিহীন হওয়া উচিত, যখন তাদের যৌগিক কার্যনির্বাহী অংশগুলি ( +=
ইত্যাদি) তাদের বাম যুক্তি পরিবর্তন করে একটি সদস্য হওয়া উচিত। এখানে জন্য দৃষ্টান্তমূলক কোড +=
এবং +
; অন্যান্য বাইনারি পাটিগণিত অপারেটর একই পদ্ধতিতে প্রয়োগ করা উচিত:
class X {
X& operator+=(const X& rhs)
{
// actual addition of rhs to *this
return *this;
}
};
inline X operator+(X lhs, const X& rhs)
{
lhs += rhs;
return lhs;
}
operator+=
রেফারেন্স অনুযায়ী operator+
তার ফলাফল প্রদান করে, তার ফলাফলের একটি অনুলিপি প্রদান করে। অবশ্যই, কোনও রেফারেন্স ফিরিয়ে দেওয়া সাধারণত অনুলিপি ফেরত দেওয়ার চেয়ে বেশি দক্ষ, তবে এর ক্ষেত্রে operator+
অনুলিপি করার কোনও উপায় নেই। আপনি যখন লিখবেন a + b
, আপনি ফলাফলটি একটি নতুন মান হিসাবে প্রত্যাশা করবেন, যার কারণে operator+
একটি নতুন মান ফিরে আসতে হবে। ৩
এছাড়াও নোট করুন যে কনস্ট্রিপ রেফারেন্সের পরিবর্তে অনুলিপি দ্বারাoperator+
বাম অপারেণ্ডটি নিয়েছে । এর কারণ অনুলিপি অনুসারে তার যুক্তি নেওয়ার কারণ হিসাবে একই ।operator=
বিট ম্যানিপুলেশন অপারেটরগুলি ~
&
|
^
<<
>>
গণিত অপারেটরগুলির মতো একইভাবে প্রয়োগ করা উচিত। তবে, (ওভারলোডিং <<
এবং >>
আউটপুট এবং ইনপুট ব্যতীত) এগুলি অতিরিক্ত লোড করার জন্য যুক্তিসঙ্গত ব্যবহারের খুব কম কেস রয়েছে।
৩ আবার এ থেকে যে পাঠ গ্রহণ করা হবে তা a += b
হ'ল, সাধারণভাবে তার চেয়েও বেশি দক্ষ a + b
এবং যদি সম্ভব হয় তবে তাকে অগ্রাধিকার দেওয়া উচিত।
অ্যারে সাবস্ক্রিপশন
অ্যারে সাবস্ক্রিপ্ট অপারেটর একটি বাইনারি অপারেটর যা অবশ্যই শ্রেণীর সদস্য হিসাবে প্রয়োগ করা উচিত। এটি ধারক-জাতীয় ধরণের জন্য ব্যবহৃত হয় যা কোনও কী দ্বারা তাদের ডেটা উপাদানগুলিতে অ্যাক্সেসের অনুমতি দেয়। এগুলি সরবরাহের আধ্যাত্মিক রূপটি হ'ল:
class X {
value_type& operator[](index_type idx);
const value_type& operator[](index_type idx) const;
// ...
};
আপনি না চাইলে আপনার শ্রেণীর ব্যবহারকারীরা ফিরে আসা ডেটা উপাদানগুলি পরিবর্তন করতে সক্ষম হবেন না operator[]
(যদি আপনি নন-কনস্ট্যান্ট বৈকল্পিক বাদ দিতে পারেন), আপনার সর্বদা অপারেটরের উভয় রূপই সরবরাহ করা উচিত।
যদি মান_প্রকারটি একটি অন্তর্নির্মিত প্রকারের জন্য উল্লেখ করা হয়, তবে অপারেটরের কনস্টেন্ট রূপটি কনস্ট কনফারেন্সের পরিবর্তে একটি অনুলিপি আরও ভালভাবে ফিরিয়ে আনতে হবে:
class X {
value_type& operator[](index_type idx);
value_type operator[](index_type idx) const;
// ...
};
পয়েন্টার-জাতীয় টাইপের জন্য অপারেটর
আপনার নিজের পুনরাবৃত্তকারী বা স্মার্ট পয়েন্টারগুলি সংজ্ঞায়িত করার জন্য আপনাকে ইউনারি প্রিফিক্স ডেরিফারেন্স অপারেটর *
এবং বাইনারি ইনফিক্স পয়েন্টার সদস্য অ্যাক্সেস অপারেটরটি ওভারলোড করতে হবে ->
:
class my_ptr {
value_type& operator*();
const value_type& operator*() const;
value_type* operator->();
const value_type* operator->() const;
};
নোট করুন যে এগুলিও প্রায় সর্বদা একটি কনস্ট এবং একটি অবিচ্ছিন্ন সংস্করণ উভয়েরই প্রয়োজন। জন্য ->
অপারেটর, যদি value_type
হয় class
(অথবা struct
অথবা union
আরেকটি) টাইপ operator->()
যাও recursively বলা হয়, একটি পর্যন্ত operator->()
আয় অ বর্গ ধরনের মান।
অবিচ্ছিন্ন ঠিকানা-অপারেটরটি কখনই ওভারলোড করা উচিত নয়।
জন্য operator->*()
দেখতে এই প্রশ্নের । এটি খুব কমই ব্যবহৃত হয় এবং এর ফলে খুব কমই কখনও কখনও বোঝা যায়। আসলে, এমনকি পুনরাবৃত্তিকারীরা এটি ওভারলোড করে না।
রূপান্তর অপারেটরগুলিতে চালিয়ে যান