সর্বাধিক গুরুত্বপূর্ণ প্রশ্ন প্রথম:
সি ++ তে প্যারামিটারগুলি কীভাবে প্রেরণ করা যায় সে সম্পর্কে কোনও সর্বোত্তম অনুশীলন রয়েছে কারণ আমি এটি সত্যিই খুঁজে পাই, আসুন ধরা যাক, তুচ্ছ নয়
যদি আপনার ফাংশনটিতে মূল অবজেক্টটি পাস করার সংশোধন করা দরকার , যাতে কলটি ফিরে আসার পরে সেই বস্তুর পরিবর্তনগুলি কলারের কাছে দৃশ্যমান হয়, তবে আপনার অবকাশ মূল্য উল্লেখ করা উচিত :
void foo(my_class& obj)
{
// Modify obj here...
}
যদি আপনার ফাংশনটির মূল অবজেক্টটি সংশোধন করার প্রয়োজন না হয় এবং এর একটি অনুলিপি তৈরি করার প্রয়োজন না হয় (অন্য কথায়, এটির কেবল এটির অবস্থা পর্যবেক্ষণ করা দরকার), তবে আপনাকে নিম্নলিখিত বিষয়গুলি উল্লেখ করতে হবেconst
:
void foo(my_class const& obj)
{
// Observe obj here
}
এটি আপনাকে ফাংশনটি উভয়কে লভ্যালু (কলমগুলির সাথে স্থিতিশীল পরিচয়যুক্ত বস্তু) এবং মূল্যগুলির সাথে কল করতে দেয় (মূল্যগুলি হ'ল উদাহরণস্বরূপ অস্থায়ী উদাহরণস্বরূপ , বা কলিংয়ের ফলাফল হিসাবে আপনি যে পদক্ষেপগুলি থেকে সরে যাচ্ছেন std::move()
)।
তোলা যায় তর্ক হতে পারে মৌলিক ধরনের বা ধরনের, যার জন্য কপি দ্রুত জন্য যেমন int
, bool
অথবা char
সেখানে ফাংশন কেবল মান পালন করা প্রয়োজন, এবং যদি রেফারেন্স দ্বারা পাস কোন প্রয়োজন নেই মান পাশ বিশেষ সুবিধাপ্রাপ্ত হবে । এটি সঠিক যদি রেফারেন্স শব্দার্থবিজ্ঞানের প্রয়োজন হয় না, তবে যদি ফাংশনটি কোথাও কোথাও সেই একই ইনপুট অবজেক্টে একটি পয়েন্টার সংরক্ষণ করতে চায়, যাতে ভবিষ্যতে সেই পয়েন্টারের মধ্য দিয়ে পড়লে সেই মান পরিবর্তনগুলি দেখা যাবে যা অন্য কোনও অংশে সম্পাদিত হয়েছে কোড? এই ক্ষেত্রে, রেফারেন্স দ্বারা পাস করা সঠিক সমাধান।
যদি আপনার ফাংশনটিতে মূল অবজেক্টটি সংশোধন করার প্রয়োজন না হয় তবে সেই অবজেক্টের একটি অনুলিপি সংরক্ষণ করতে হয় ( সম্ভবত ইনপুটটি পরিবর্তন না করে ইনপুটটির পরিবর্তনের ফলাফলটি ফেরত দিতে পারে ), তবে আপনি মান অনুসারে বিবেচনা করতে পারেন :
void foo(my_class obj) // One copy or one move here, but not working on
// the original object...
{
// Working on obj...
// Possibly move from obj if the result has to be stored somewhere...
}
উপরের ক্রিয়াকলাপটি চালিয়ে যাওয়ার ফলে লভ্যালুগুলি পাস করার সময় সর্বদা একটি অনুলিপি হবে এবং মূল্যগুলি পাস করার সময় একটি চলবে। আপনার ফাংশন এই বস্তুর কোথাও সঞ্চয় করতে চাইলে, আপনি একটি অতিরিক্ত সঞ্চালন পারে পদক্ষেপ তা থেকে (উদাহরণস্বরূপ, যদি foo()
হয় একজন সদস্য ফাংশন যা চাহিদা একটি ডাটা সদস্য মান সংরক্ষণ করতে )।
যদি পদক্ষেপগুলি ধরণের অবজেক্টগুলির জন্য ব্যয়বহুল হয়my_class
, তবে আপনি ওভারলোডিং বিবেচনা করতে পারেন foo()
এবং ল্যাভালুগুলির জন্য একটি সংস্করণ সরবরাহ করতে পারেন (যার সাথে একটি মূল্য মূল্য উল্লেখ করা হয় const
) এবং মূল্যগুলির জন্য একটি সংস্করণ (কোনও মূল্যবান রেফারেন্স গ্রহণ করা):
// Overload for lvalues
void foo(my_class const& obj) // No copy, no move (just reference binding)
{
my_class copyOfObj = obj; // Copy!
// Working on copyOfObj...
}
// Overload for rvalues
void foo(my_class&& obj) // No copy, no move (just reference binding)
{
my_class copyOfObj = std::move(obj); // Move!
// Notice, that invoking std::move() is
// necessary here, because obj is an
// *lvalue*, even though its type is
// "rvalue reference to my_class".
// Working on copyOfObj...
}
উপরের ফাংশনগুলি এতটা সাদৃশ্যপূর্ণ, বাস্তবে, আপনি এটির বাইরে একটি ফাংশন তৈরি করতে পারেন: foo()
একটি ফাংশন টেম্পলেট হয়ে উঠতে পারে এবং অবজেক্টের কোনও স্থানান্তর বা একটি অনুলিপি অভ্যন্তরীণভাবে উত্পন্ন হবে কিনা তা নির্ধারণের জন্য আপনি নিখুঁত ফরওয়ার্ডিং ব্যবহার করতে পারেন :
template<typename C>
void foo(C&& obj) // No copy, no move (just reference binding)
// ^^^
// Beware, this is not always an rvalue reference! This will "magically"
// resolve into my_class& if an lvalue is passed, and my_class&& if an
// rvalue is passed
{
my_class copyOfObj = std::forward<C>(obj); // Copy if lvalue, move if rvalue
// Working on copyOfObj...
}
স্কট মেয়ার্সের এই টকটি দেখে আপনি এই নকশাটি সম্পর্কে আরও জানতে চাইতে পারেন (কেবল " ইউনিভার্সাল রেফারেন্স " যে শব্দটি তিনি ব্যবহার করছেন তা মানা করুন)।
একটি বিষয় মনে রাখবেন তা হ'ল std::forward
সাধারনত মূল্যবোধের পদক্ষেপে শেষ হয় , তাই এটি তুলনামূলকভাবে নির্দোষ বলে মনে হলেও একই বস্তুকে একাধিকবার ফরোয়ার্ড করা সমস্যার কারণ হতে পারে - উদাহরণস্বরূপ, একই বস্তু থেকে দু'বার সরে যাওয়া! সুতরাং এটিকে কোনও লুপে না ফেলতে এবং একই যুক্তিটি একাধিকবার ফাংশন কলে প্রেরণ না করার বিষয়ে সতর্ক থাকুন:
template<typename C>
void foo(C&& obj)
{
bar(std::forward<C>(obj), std::forward<C>(obj)); // Dangerous!
}
এছাড়াও লক্ষ করুন, আপনি সাধারণত টেমপ্লেট-ভিত্তিক সমাধানটি অবলম্বন করবেন না যদি না আপনি যদি এটির পক্ষে যুক্তিযুক্ত কারণ এটি আপনার কোডটি পড়া শক্ত করে তোলে। সাধারণত, আপনার স্পষ্টতা এবং সরলতার উপর ফোকাস করা উচিত ।
উপরেরগুলি কেবল সহজ গাইডলাইন, তবে বেশিরভাগ সময় তারা আপনাকে ভাল ডিজাইনের সিদ্ধান্তের দিকে নির্দেশ করবে।
আপনার পোস্টের বিশ্রামটি বিবেচনা করা:
যদি আমি এটিকে আবার লিখি [...] তবে 2 টি চাল চলবে এবং কোনও অনুলিপি থাকবে না।
এটি সঠিক নয়। শুরুতে, কোনও মূল্যের রেফারেন্স কোনও লভালুকে আবদ্ধ করতে পারে না, সুতরাং এটি কেবল তখনই সঙ্কলিত হবে যখন আপনি CreditCard
আপনার নির্মাতাকে টাইপের কোনও মূল্য পাস করছেন passing এই ক্ষেত্রে:
// Here you are passing a temporary (OK! temporaries are rvalues)
Account acc("asdasd",345, CreditCard("12345",2,2015,1001));
CreditCard cc("12345",2,2015,1001);
// Here you are passing the result of std::move (OK! that's also an rvalue)
Account acc("asdasd",345, std::move(cc));
তবে আপনি এটি করার চেষ্টা করলে এটি কার্যকর হবে না:
CreditCard cc("12345",2,2015,1001);
Account acc("asdasd",345, cc); // ERROR! cc is an lvalue
কারণ cc
একটি মূল্যবান এবং মূল্যসূত্রের উল্লেখগুলি লভ্যুকে আবদ্ধ করতে পারে না। তদ্ব্যতীত, যখন কোনও সামগ্রীর রেফারেন্সকে বাঁধাই করা হয় , তখন কোনও পদক্ষেপ নেওয়া হয় না : এটি কেবল একটি রেফারেন্স বাইন্ডিং। সুতরাং, কেবল একটি পদক্ষেপ থাকবে।
সুতরাং এই উত্তরের প্রথম অংশে প্রদত্ত গাইডলাইনগুলির উপর ভিত্তি করে, আপনি যখন CreditCard
মান দ্বারা মূল্য গ্রহণের সময় উত্পন্ন পদক্ষেপের সংখ্যা সম্পর্কে উদ্বিগ্ন হন , আপনি দুটি কনস্ট্রাক্টর ওভারলোডগুলি সংজ্ঞায়িত করতে পারেন, একটি const
( CreditCard const&
) এর মূল্য মূল্য উল্লেখ এবং একটি গ্রহণ একটি মূল রেফারেন্স ( CreditCard&&
)।
ওভারলোড রেজোলিউশন একটি লভ্যালু পাস করার সময় পূর্বেরটিকে নির্বাচন করবে (এই ক্ষেত্রে, একটি অনুলিপি সম্পাদন করা হবে) এবং পরে যখন কোনও মূল্য পাস করার সময় (এই ক্ষেত্রে, একটি পদক্ষেপ করা হবে)।
Account(std::string number, float amount, CreditCard const& creditCard)
: number(number), amount(amount), creditCard(creditCard) // copy here
{ }
Account(std::string number, float amount, CreditCard&& creditCard)
: number(number), amount(amount), creditCard(std::move(creditCard)) // move here
{ }
std::forward<>
আপনি নিখুঁত ফরওয়ার্ডিং অর্জন করতে চাইলে আপনার ব্যবহারটি সাধারণত দেখা যায় । সেক্ষেত্রে আপনার কনস্ট্রাক্টরটি আসলে কনস্ট্রাক্টর টেম্পলেট হবে এবং নীচে আরও কম দেখায়
template<typename C>
Account(std::string number, float amount, C&& creditCard)
: number(number), amount(amount), creditCard(std::forward<C>(creditCard)) { }
এক অর্থে, এই সম্মিলন উভয় overloads আমি একটি একক কার্যকারিতা মধ্যে পূর্বে দেখানো করেছি: C
হতে অনুমিত করা হবে CreditCard&
যদি আপনি একটি lvalue ক্ষণস্থায়ী হয়, এবং রেফারেন্স নিয়ম ধ্বসে কারণে, এই ফাংশন instantiated করা যাবে না:
Account(std::string number, float amount, CreditCard& creditCard) :
number(num), amount(amount), creditCard(std::forward<CreditCard&>(creditCard))
{ }
এই কারণ হবে কপি-নির্মাণ এর creditCard
হিসাবে আপনি ইচ্ছুক হবে। অন্যদিকে, যখন কোনও মূল্যায়ন পাস হয়ে যায় তখন তা হ্রাস করা C
হবে CreditCard
এবং পরিবর্তে এই ফাংশনটি তাত্ক্ষণিকভাবে শুরু করা হবে:
Account(std::string number, float amount, CreditCard&& creditCard) :
number(num), amount(amount), creditCard(std::forward<CreditCard>(creditCard))
{ }
এই কারণ হবে পদক্ষেপ নির্মাণাধীন এর creditCard
, যা আপনি যা চান তা (কারণ মান প্রেরণ করা হচ্ছে একটি rvalue, এবং যে উপায়ে আমরা এটা থেকে সরাতে অনুমোদিত হয়)।