সঠিকভাবে প্যারামিটারগুলি কীভাবে পাস করবেন?


108

আমি একজন সি ++ শিক্ষানবিস তবে প্রোগ্রামিং শিক্ষানবিশ নই। আমি সি ++ (সি ++ 11) শেখার চেষ্টা করছি এবং এটি আমার পক্ষে সবচেয়ে গুরুত্বপূর্ণ বিষয়টি স্পষ্ট নয়: পরামিতিগুলি পাস করা।

আমি এই সহজ উদাহরণ বিবেচনা:

  • এমন একটি শ্রেণী যা এর সমস্ত সদস্যের আদিম ধরণের রয়েছে:
    CreditCard(std::string number, int expMonth, int expYear,int pin):number(number), expMonth(expMonth), expYear(expYear), pin(pin)

  • এমন এক শ্রেণীর সদস্য হিসাবে আদিম প্রকার +1 জটিল প্রকার:
    Account(std::string number, float amount, CreditCard creditCard) : number(number), amount(amount), creditCard(creditCard)

  • এমন একটি শ্রেণীর সদস্য হিসাবে কিছু জটিল ধরণের +1 সংগ্রহের সদস্য রয়েছে: Client(std::string firstName, std::string lastName, std::vector<Account> accounts):firstName(firstName), lastName(lastName), accounts(accounts)

আমি যখন একটি অ্যাকাউন্ট তৈরি করি, আমি এটি করি:

    CreditCard cc("12345",2,2015,1001);
    Account acc("asdasd",345, cc);

অবশ্যই এই দৃশ্যে ক্রেডিট কার্ডটি দু'বার অনুলিপি করা হবে। আমি যদি সেই কনস্ট্রাক্টরকে আবার লিখি

Account(std::string number, float amount, CreditCard& creditCard) 
    : number(number)
    , amount(amount)
    , creditCard(creditCard)

একটি অনুলিপি থাকবে আমি যদি এটি আবার লিখি

Account(std::string number, float amount, CreditCard&& creditCard) 
    : number(number)
    , amount(amount)
    , creditCard(std::forward<CreditCard>(creditCard))

2 টি চাল এবং কোনও অনুলিপি থাকবে।

আমি মনে করি কখনও কখনও আপনি কিছু প্যারামিটার অনুলিপি করতে চাইতে পারেন, কখনও কখনও আপনি যখন সেই বস্তুটি তৈরি করেন তখন অনুলিপি করতে চান না।
আমি সি # থেকে এসেছি এবং রেফারেন্সগুলিতে অভ্যস্ত হয়েছি, এটি আমার কাছে কিছুটা অদ্ভুত এবং আমি মনে করি প্রতিটি প্যারামিটারের জন্য 2 ওভারলোড হওয়া উচিত তবে আমি জানি আমি ভুল am
সি ++ তে প্যারামিটারগুলি কীভাবে প্রেরণ করা যায় সে সম্পর্কে কোনও সর্বোত্তম অনুশীলন রয়েছে কারণ আমি এটি সত্যিই খুঁজে পাই, আসুন ধরা যাক, তুচ্ছ নয়। আপনি আমার উপস্থাপিত উদাহরণগুলি কীভাবে পরিচালনা করবেন?


9
মেটা: আমি বিশ্বাস করতে পারি না যে কেউ কেবল একটি ভাল সি ++ প্রশ্ন করেছে। +1 টি।

23
এফওয়াইআই, std::stringএকটি শ্রেণি, ঠিক যেমন CreditCardকোনও আদিম ধরণের নয়।
ক্রিস

7
স্ট্রিং বিভ্রান্তির কারণে, যদিও এটি সম্পর্কিত নয়, আপনার জানা উচিত যে একটি স্ট্রিং আক্ষরিক,, "abc"টাইপের std::stringনয়, টাইপের char */const char *নয়, টাইপের const char[N](তিনটি অক্ষর এবং একটি নালীর কারণে এই ক্ষেত্রে এন = 4 সহ)) উপায় থেকে বেরিয়ে আসার জন্য এটি একটি ভাল, সাধারণ ভুল ধারণা।
ক্রিস

10
@ চ্যুেক্স: জন স্কিটি পুরো # টি প্রশ্নেই রয়েছে, খুব কমই সি ++ রয়েছে।
স্টিভ জেসোপ

উত্তর:


158

সর্বাধিক গুরুত্বপূর্ণ প্রশ্ন প্রথম:

সি ++ তে প্যারামিটারগুলি কীভাবে প্রেরণ করা যায় সে সম্পর্কে কোনও সর্বোত্তম অনুশীলন রয়েছে কারণ আমি এটি সত্যিই খুঁজে পাই, আসুন ধরা যাক, তুচ্ছ নয়

যদি আপনার ফাংশনটিতে মূল অবজেক্টটি পাস করার সংশোধন করা দরকার , যাতে কলটি ফিরে আসার পরে সেই বস্তুর পরিবর্তনগুলি কলারের কাছে দৃশ্যমান হয়, তবে আপনার অবকাশ মূল্য উল্লেখ করা উচিত :

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, এবং যে উপায়ে আমরা এটা থেকে সরাতে অনুমোদিত হয়)।


অ-আদিম প্যারামিটার ধরণের জন্য এটি কি ভুল বলা যায় যে সর্বদা ফরওয়ার্ড সহ টেম্পলেট সংস্করণটি ব্যবহার করা ঠিক?
জ্যাক উইলসন

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

2
আমি মনে করি তৃতীয় কোডের নমুনায় সরানোর দরকার নেই। আপনি কেবল objস্থানীয় কপি তৈরির পরিবর্তে ব্যবহার করতে পারেন এতে ?
juanchopanza

3
@ অ্যান্ডিপ্রল: আপনি আমার +1 ঘন্টা আগে পেয়েছিলেন, তবে আপনার (দুর্দান্ত) উত্তরটি পুনরায় পড়া, আমি দুটি বিষয় তুলে ধরতে চাই: ক) সর্বদা মান সর্বদা একটি অনুলিপি তৈরি করে না (যদি তা সরানো না হয়)। কলকারীদের সাইটটিতে জায়গাটি নির্মান করা যায়, বিশেষত আরভিও / এনআরভিওর সাথে এটি এমনকি প্রথম ভাবার চেয়ে বেশি কাজ করে। খ) দয়া করে উল্লেখ করুন যে শেষ উদাহরণে কেবল একবারstd::forward ডাকা যেতে পারে । আমি লোকেদের এগুলিতে ডুবিয়ে রাখা ইত্যাদি দেখেছি এবং যেহেতু এই উত্তরটি প্রচুর প্রাথমিকের দ্বারা দেখা যাবে, তাই এই ফাঁদ এড়াতে তাদের সাহায্য করার জন্য লেবেল একটি চর্বিযুক্ত "সতর্কতা!" হওয়া উচিত।
ড্যানিয়েল ফ্রে

1
@ স্টিভ জেসাপ: এবং এগুলি ছাড়াও, প্রযুক্তিগত সমস্যা রয়েছে (অগত্যা সমস্যা নয়) ফরোয়ার্ডিং ফাংশনগুলি রয়েছে ( বিশেষত নির্মাতারা একটি যুক্তি গ্রহণ করে ), বেশিরভাগ ক্ষেত্রে তারা যে কোনও ধরণের যুক্তি গ্রহণ করে এবং std::is_constructible<>প্রকার বৈশিষ্ট্যকে পরাস্ত করতে পারে যদি না তারা যথাযথভাবে SFINAE- হয়। সীমাবদ্ধ - যা কারও পক্ষে তুচ্ছ হতে পারে না।
অ্যান্ডি প্রোল

11

প্রথমে আমাকে কিছু বিশদ সংশোধন করি। আপনি যখন নিম্নলিখিতটি বলবেন:

2 চাল এবং কোন অনুলিপি থাকবে।

এটা মিথ্যা। কোনও মূল্যের রেফারেন্সের সাথে বাঁধাই কোনও চাল নয়। একটি পদক্ষেপ আছে।

অতিরিক্তভাবে, যেহেতু CreditCardকোনও টেম্পলেট প্যারামিটার নয়, std::forward<CreditCard>(creditCard)এটি কেবল বলার একটি ভারবজ পদ্ধতি std::move(creditCard)

এখন ...

যদি আপনার ধরণের "সস্তা" চাল থাকে তবে আপনি নিজের জীবনকে সহজ করে তুলতে এবং মূল্য দিয়ে এবং " std::moveপাশাপাশি" সমস্ত কিছু নিতে চাইতে পারেন ।

Account(std::string number, float amount, CreditCard creditCard)
: number(std::move(number),
  amount(amount),
  creditCard(std::move(creditCard)) {}

যখন কেবলমাত্র একটিই ফলন করতে পারে তখন এই পদ্ধতিটি আপনাকে দুটি চাল দেবে, তবে যদি চালগুলি কম হয় তবে সেগুলি গ্রহণযোগ্য হতে পারে।

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

আপনি যদি এই অতিরিক্ত পদক্ষেপগুলি ব্যয় করতে না চান তবে কী করবেন? হতে পারে এগুলি খুব ব্যয়বহুল, বা আরও খারাপ প্রমাণিত হতে পারে সম্ভবত প্রকারগুলি সরানো যায় না এবং আপনার অতিরিক্ত অনুলিপিও থাকতে পারে।

যদি কেবল একটি সমস্যাযুক্ত পরামিতি থাকে তবে আপনি দুটি ওভারলোড সরবরাহ করতে পারেন T const&এবং এর সাথে T&&। এটি প্রকৃত সদস্য সূচনা পর্যন্ত সমস্ত সময় রেফারেন্সকে আবদ্ধ করে রাখে, যেখানে অনুলিপি বা সরানো ঘটে।

তবে, আপনার যদি একাধিক প্যারামিটার থাকে তবে এটি ওভারলোডের সংখ্যায় ঘনিষ্ঠভাবে বিস্ফোরণ ঘটায়।

এটি এমন একটি সমস্যা যা নিখুঁত ফরওয়ার্ডিংয়ের মাধ্যমে সমাধান করা যায়। এর অর্থ আপনি পরিবর্তে একটি টেম্পলেট লিখেছেন এবং std::forwardসদস্য হিসাবে তাদের চূড়ান্ত গন্তব্যে যুক্তির মান বিভাগের সাথে বহন করতে ব্যবহার করেন to

template <typename TString, typename TCreditCard>
Account(TString&& number, float amount, TCreditCard&& creditCard)
: number(std::forward<TString>(number),
  amount(amount),
  creditCard(std::forward<TCreditCard>(creditCard)) {}

টেমপ্লেট সংস্করণটিতে একটি সমস্যা রয়েছে: ব্যবহারকারী Account("",0,{brace, initialisation})আর লিখতে পারবেন না।
আইপিসি

@ আইপিসি আহ, সত্য। এটি সত্যই বিরক্তিকর, এবং আমি মনে করি না যে কোনও সহজ স্কেলযোগ্য কর্মসংস্থান আছে।
আর মার্টিনহো ফার্নান্দেস

6

প্রথমত, std::stringবেশ একটি বিশাল শ্রেণীর ধরণের মত std::vector। এটি অবশ্যই আদিম নয়।

যদি আপনি কোনও নির্মাণকারীর কাছে মান অনুসারে কোনও বড় চলনীয় প্রকারগুলি গ্রহণ করেন তবে আমি std::moveসেগুলিকে সদস্য করে দেব :

CreditCard(std::string number, float amount, CreditCard creditCard)
  : number(std::move(number)), amount(amount), creditCard(std::move(creditCard))
{ }

ঠিক এইভাবেই আমি কনস্ট্রাক্টর বাস্তবায়নের পরামর্শ দেব। এটা সদস্যদের কারণnumber এবং creditCardঅনুলিপিটি তৈরির পরিবর্তে কপিরাইট তৈরির কারণ হয়ে থাকে। আপনি যখন এই কনস্ট্রাক্টরটি ব্যবহার করবেন তখন অবজেক্টটি কনস্ট্রাক্টরের কাছে যাওয়ার পরে একটি কপি থাকবে (বা স্থানান্তর, যদি অস্থায়ী হয়) এবং সদস্যটির সূচনা করার পরে একটি পদক্ষেপ থাকবে।

এখন এই নির্মাতা বিবেচনা করুন:

Account(std::string number, float amount, CreditCard& creditCard)
  : number(number), amount(amount), creditCard(creditCard)

আপনি ঠিক বলেছেন, creditCardএটির একটি অনুলিপি জড়িত হবে , কারণ এটি প্রথমে কনস্ট্রাক্টরকে রেফারেন্স দ্বারা প্রেরণ করা হয়। তবে এখন আপনি constকনস্ট্রাক্টরের কাছে অবজেক্টগুলি পাস করতে পারবেন না (কারণ রেফারেন্সটি অ- const) এবং আপনি অস্থায়ী বস্তুগুলি পাস করতে পারবেন না। উদাহরণস্বরূপ, আপনি এটি করতে পারেন নি:

Account account("something", 10.0f, CreditCard("12345",2,2015,1001));

এখন বিবেচনা করা যাক:

Account(std::string number, float amount, CreditCard&& creditCard)
  : number(number), amount(amount), creditCard(std::forward<CreditCard>(creditCard))

এখানে আপনি মূল্যের উল্লেখগুলি এবং এর একটি ভুল বোঝাবুঝি দেখিয়েছেন std::forward। আপনি কেবল তখনই ব্যবহার করা উচিত std::forwardযখন আপনি ফরোয়ার্ড করা অবজেক্টটি T&&কিছু অনুমিত প্রকার হিসাবে ঘোষণা করা হয় T। এখানে CreditCardছাড় করা হয় না (আমি ধরে নিচ্ছি), এবং তাই এটি std::forwardত্রুটি হিসাবে ব্যবহৃত হচ্ছে। দেখুন সার্বজনীন রেফারেন্স


1

আমি সাধারণ ক্ষেত্রে খুব সহজ নিয়ম ব্যবহার করি: পিওডির জন্য অনুলিপি (ইনট, বুল, ডাবল, ...) এবং কনস্ট এবং সমস্ত কিছুর জন্য ব্যবহার করুন ...

এবং অনুলিপি করতে বা না চাওয়ার জন্য, পদ্ধতি স্বাক্ষরের দ্বারা উত্তর দেওয়া হয় না তবে আপনি পরামিতিগুলির সাথে কী করেন by

struct A {
  A(const std::string& aValue, const std::string& another) 
    : copiedValue(aValue), justARef(another) {}
  std::string copiedValue;
  const std::string& justARef; 
};

পয়েন্টারের জন্য নির্ভুলতা: আমি এগুলি প্রায় কখনও ব্যবহার করি না। কেবলমাত্র ও বেশি সুবিধা হ'ল এগুলি নাল, বা পুনরায় বরাদ্দ করা যেতে পারে।


2
"আমি সাধারণ ক্ষেত্রে খুব সহজ নিয়ম ব্যবহার করি: পিওডির জন্য অনুলিপি (ইনট, বুল, ডাবল, ...) এবং কনস্ট এবং অন্য কিছুর জন্য ব্যবহার করুন।" নং জাস্ট নং
জুতো

আপনি যদি কোনও & (কোনও কনস্ট্যান্ড) ব্যবহার করে কোনও মান পরিবর্তন করতে চান তবে যুক্ত হতে পারে। অন্যথায় আমি দেখতে পাচ্ছি না ... সরলতা যথেষ্ট ভাল। তবে আপনি যদি তাই বলেছেন ...
ডেভিড ফ্ল্যুরি

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

ঠিক আছে, এটি আমি পরে যুক্ত করেছি have খুব দ্রুত উত্তর হতে পারে।
ডেভিড ফ্লুরি

1

এটি আমার কাছে সবচেয়ে গুরুত্বপূর্ণ বিষয়টি অস্পষ্ট: পরামিতিগুলি পেরিয়ে যাওয়া।

  • আপনি যদি ফাংশন / পদ্ধতির অভ্যন্তরে ভেরিয়েবলটি পরিবর্তন করতে চান
    • আপনি এটি রেফারেন্স দিয়ে পাস
    • আপনি এটি একটি পয়েন্টার হিসাবে পাস (*)
  • আপনি যদি ফাংশন / পদ্ধতির ভিতরে পাস করা মান / ভেরিয়েবলটি পড়তে চান তবে
    • আপনি কনট রেফারেন্স দ্বারা এটি পাস
  • আপনি যদি ফাংশন / পদ্ধতির অভ্যন্তরে পাস করা মানটি সংশোধন করতে চান
    • আপনি অবজেক্টটি (**) অনুলিপি করে এটি সাধারণত পাস করুন

(*) পয়েন্টারগুলি গতিশীলভাবে বরাদ্দকৃত মেমরিটিকে বোঝায়, অতএব সম্ভব হলে আপনার পয়েন্টারগুলির তুলনায় রেফারেন্সগুলি অগ্রাধিকার দেওয়া উচিত এমনকি শেষ পর্যন্ত রেফারেন্স হিসাবে সাধারণত প্রয়োগ করা হয় implemented

(**) "সাধারনত" অর্থ কপি কনস্ট্রাক্টর (যদি আপনি একই ধরণের প্যারামিটারের কোনও বস্তু পাস করেন) বা সাধারণ কনস্ট্রাক্টর (যদি আপনি ক্লাসের জন্য একটি সামঞ্জস্যপূর্ণ টাইপ পাস করেন) দ্বারা বোঝায়। myMethod(std::string)উদাহরণস্বরূপ, আপনি যখন কোনও বস্তু পাস করেন , উদাহরণস্বরূপ, অনুলিপিটি সরবরাহ করা হলে অনুলিপিটি ব্যবহার std::stringকরা হবে, সুতরাং আপনাকে অবশ্যই এটির উপস্থিতি নিশ্চিত করতে হবে।

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