c++ - tutorial - সি++ প্রোগ্রামিং বই




কিভাবে C++ ফাংশন বস্তু পাস করতে? (5)

আমি সি ++ প্রোগ্রামিংতে নতুন, কিন্তু জাভাতে আমার অভিজ্ঞতা আছে। C ++ এ ফাংশনগুলিতে বস্তুগুলি কীভাবে পাস করতে হয় সে সম্পর্কে নির্দেশিকা প্রয়োজন।

আমি পয়েন্টার, রেফারেন্স, বা অ পয়েন্টার এবং অ রেফারেন্স মান পাস করতে হবে? আমি জাভাতে মনে রাখি যে কোনও সমস্যা নেই কারণ আমরা বস্তুর রেফারেন্স ধারণ করে এমন ভেরিয়েবল পাস করি।

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


সি ++ 11 এর জন্য থাম্ব রুলস :

মান দ্বারা পাস, যখন ছাড়া

  1. আপনি বস্তুর মালিকানা প্রয়োজন এবং একটি সহজ উদীয়মান করবেন না, ক্ষেত্রে আপনি const রেফারেন্স দ্বারা পাস ,
  2. আপনি বস্তুর পরিবর্তন করতে হবে, কোন ক্ষেত্রে, একটি অ-কনস্টেবল lvalue রেফারেন্স দ্বারা পাস ব্যবহার,
  3. আপনি বেস ক্লাস হিসাবে প্রাপ্ত শ্রেণীর বস্তু পাস, ক্ষেত্রে আপনি রেফারেন্স দ্বারা পাস করতে হবে । ( const রেফারেন্স দ্বারা পাস কিনা তা নির্ধারণ করতে পূর্ববর্তী নিয়মগুলি ব্যবহার করুন।)

পয়েন্টার দ্বারা পাস কার্যত পরামর্শ দেওয়া হয় না। বিকল্প পরামিতিগুলি std::optional (পুরানো std libs জন্য boost::optional ) হিসাবে ভালভাবে প্রকাশ করা হয় এবং এলিয়াসিং রেফারেন্স দ্বারা জরিমানা করা হয়।

সি ++ 11 এর সরানো শব্দটি জটিল বস্তুর জন্য এমনকি আরো আকর্ষণীয় করে পাশ দিয়ে এবং ফেরত করে।

সি ++ 03 জন্য থাম্ব এর নিয়ম :

const রেফারেন্স দ্বারা আর্গুমেন্ট পাস, যখন ছাড়া

  1. তারা ফাংশনের ভেতরে পরিবর্তন করতে হবে এবং এই ধরনের পরিবর্তনগুলি বাইরে প্রতিফলিত হওয়া উচিত, কোন ক্ষেত্রে আপনি অ-রেফারেন্স দ্বারা পাস করেন
  2. ফাংশন কোন যুক্তি ছাড়াই কলযোগ্য হতে হবে, কোন ক্ষেত্রে আপনি পয়েন্টার দ্বারা পাস, যাতে ব্যবহারকারী পরিবর্তে NULL / 0 / nullptr পাস করতে পারে; আপনি একটি const একটি পয়েন্টার দ্বারা পাস করা উচিত কিনা তা নির্ধারণ করতে পূর্ববর্তী নিয়ম প্রয়োগ করুন
  3. তারা অন্তর্নির্মিত ধরনের, কপি দ্বারা পাস করা যাবে যা
  4. তারা ফাংশনের ভেতরে পরিবর্তন করতে হবে এবং এই ধরনের পরিবর্তনগুলি বাইরে প্রতিফলিত করা উচিত নয় , এই ক্ষেত্রে আপনি কপি পাস করতে পারেন (একটি বিকল্প পূর্ববর্তী নিয়ম অনুযায়ী পাস করতে হবে এবং ফাংশনের ভিতরে একটি কপি তৈরি করবে)

(এখানে, "মান অনুসারে পাস করুন" বলা হয় "কপি দ্বারা পাস করুন", কারণ মান দ্বারা পাস করা সবসময় C ++ 03 এ একটি অনুলিপি তৈরি করে)

এর আরো কিছু আছে, কিন্তু এই কয়েকজন শিক্ষার নিয়ম আপনাকে অনেক দূরে নিয়ে যাবে।


মান দ্বারা পাস:

void func (vector v)

ফাংশন কার্যকর করা হলে ফাংশনটি কার্যকর করার সময় ফাংশনকে মূল পরিবর্তনশীল থেকে সংশোধন করতে এবং ফাংশনটি কার্যকর করার সময় অন্যান্য থ্রেডগুলিকে তার মান পরিবর্তন করতে বাধা দেওয়ার জন্য পরিবেশ থেকে সম্পূর্ণ বিচ্ছিন্নতা প্রয়োজন এমন মান দ্বারা পাস করুন।

নিম্নগামী CPU চক্র এবং বস্তুর অনুলিপি করতে অতিরিক্ত মেমরি ব্যয় করা হয়।

কন্স রেফারেন্স দ্বারা পাস:

void func (const vector& v);

এই ফর্মটি অনুলিপি ওভারহেড সরানোর সময় পাস-বাই-মান আচরণকে অনুকরণ করে। ফাংশন মূল বস্তুর অ্যাক্সেস পঠন পায়, কিন্তু তার মান সংশোধন করতে পারবেন না।

ডাউনসাইড থ্রেড সুরক্ষা: অন্য বস্তুর দ্বারা মূল বস্তুতে যেকোনো পরিবর্তনটি কার্যকর হওয়া পর্যন্ত ফাংশনের ভিতরে প্রদর্শিত হবে।

অ-গণিত রেফারেন্স দ্বারা পাস:

void func (vector& v)

যখন ফাংশনটিকে ভেরিয়েবলের জন্য কিছু মান লিখতে হবে তখন এটি ব্যবহার করুন, যা অবশেষে কলার দ্বারা ব্যবহৃত হবে।

কনস্ট রেফারেন্স কেসের মতো, এটি থ্রেড-নিরাপদ নয়।

কন্স পয়েন্টার দ্বারা পাস:

void func (const vector* vp);

ভিন্ন সিনট্যাক্স ব্যতীত কনফারেন্স দ্বারা পাস হিসাবে কার্যকরীভাবে একই, প্লাসটি কলিং ফাংশন নূল পয়েন্টারটি পাস করতে পারে যা ইঙ্গিত দেয় যে পাস করার জন্য কোন বৈধ তথ্য নেই।

থ্রেড-নিরাপদ নয়।

অ-পয়েন্ট পয়েন্টার দ্বারা পাস:

void func (vector* vp);

অ-গণিত রেফারেন্স অনুরূপ। ফাংশনটি একটি মান লিখতে অনুমিত না হলে কলকারী সাধারণত পরিবর্তনশীলকে NULL এ সেট করে। এই কনভেনশনটি অনেক গ্লিবসি API এ দেখা যায়। উদাহরণ:

void func (string* str, /* ... */) {
    if (str != NULL) {
        *str = some_value; // assign to *str only if it's non-null
    }
}

ঠিক মত রেফারেন্স / পয়েন্টার দ্বারা পাস, থ্রেড-নিরাপদ নয়।


C ++ এবং জাভাতে কনভেনশনগুলিতে কল করার কিছু পার্থক্য রয়েছে। সি ++-তে টেকনিক্যালি কেবলমাত্র দুটি কনভেনশনগুলি রয়েছে: পাস-বাই-মান এবং পাস-বাই-রেফারেন্স সহ, কিছু সাহিত্য সহ একটি তৃতীয় পাস-পয়েন্ট-পয়েন্টার কনভেনশন (যা আসলে পয়েন্টার টাইপের পাস-বাই-মান)। এর উপরে, আপনি শব্দটির প্রকারে কনস্টে-নেস যুক্ত করতে পারেন, সেমেটিকগুলি উন্নত করতে পারেন।

রেফারেন্স দ্বারা পাস

রেফারেন্স দ্বারা পাস মানে ফাংশন ধারণাগতভাবে আপনার বস্তু উদাহরণ পাবেন এবং এটি একটি কপি হবে না। রেফারেন্স ধারণাগতভাবে কলিং প্রেক্ষাপটে ব্যবহৃত বস্তুর একটি উপনাম, এবং নাল হতে পারে না। ফাংশন ভিতরে সঞ্চালিত সমস্ত অপারেশন ফাংশন বাইরে বস্তু প্রয়োগ। এই কনভেনশন জাভা বা সি পাওয়া যায় না।

মান দ্বারা পাস (এবং পাস দ্বারা পয়েন্টার)

কম্পাইলার কলিং প্রেক্ষাপটে বস্তুর একটি অনুলিপি জেনারেট করবে এবং ফাংশনের ভিতরে যে কপি ব্যবহার করবে। ফাংশন ভিতরে সঞ্চালিত সমস্ত অপারেশন কপি সম্পন্ন হয়, বাহ্যিক উপাদান নয়। এই জাভা আদিম ধরনের জন্য কনভেনশন।

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

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

সমীকরণ সংখ্যার যোগ করা হচ্ছে

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

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

চলতি নিয়ম

এটি অনুসরণ করার জন্য কিছু মৌলিক নিয়ম:

  • আদিম প্রকারের জন্য পাস-বাই-মান পছন্দ করুন
  • অন্যান্য ধরনের ধ্রুবক রেফারেন্স সহ পাস-বাই-রেফারেন্স পছন্দ করুন
  • ফাংশন যদি যুক্তি সংশোধন ব্যবহার প্রয়োজন পাস-বাই-রেফারেন্স
  • যদি যুক্তিটি ঐচ্ছিক হয়, পাস-বাই-পয়েন্টার ব্যবহার করুন (যদি ঐচ্ছিক মানটি সংশোধন করা উচিত না তবে ধ্রুবক)

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

সাইড নোট

C ++ এবং জাভা রেফারেন্সগুলির মধ্যে পার্থক্যের গুরুত্বকে জোর দেওয়া গুরুত্বপূর্ণ। সি ++ রেফারেন্সগুলি ধারণাগতভাবে বস্তুর উদাহরণ, এটির অ্যাক্সেসর নয়। সবচেয়ে সহজ উদাহরণ একটি সোয়াপ ফাংশন বাস্তবায়ন করা হয়:

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

উপরে সোয়াপ ফাংশন রেফারেন্স ব্যবহারের মাধ্যমে তার আর্গুমেন্ট উভয় পরিবর্তন । জাভা মধ্যে নিকটতম কোড:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

কোডটির জাভা সংস্করণ অভ্যন্তরীণভাবে রেফারেন্সগুলির কপিগুলিকে সংশোধন করবে তবে প্রকৃত বস্তুগুলিকে বাইরের দিকে সংশোধন করবে না। জাভা রেফারেন্স পয়েন্টার মধ্যে মান দ্বারা পাস পেতে পয়েন্টার গাণিতিক ছাড়া সি পয়েন্টার হয়।


একটি ফাংশন হিসাবে একটি ফাংশন একটি বস্তুর পাশ করার তিনটি পদ্ধতি আছে:

  1. রেফারেন্স দ্বারা পাস
  2. মূল্য দ্বারা পাস
  3. পরামিতি ধ্রুবক যোগ

নিম্নলিখিত উদাহরণ দিয়ে যান:

class Sample
{
public:
    int *ptr;
    int mVar;

    Sample(int i)
    {
        mVar = 4;
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }

    void PrintVal()
    {
        cout << "The value of the pointer is " << *ptr << endl
             << "The value of the variable is " << mVar;
   }
};

void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}


int main()
{

  Sample s1= 10;
  SomeFunc(s1);
  s1.PrintVal();
  char ch;
  cin >> ch;
}

আউটপুট:

বলুন আমি ফুনকিতে আছি
পয়েন্টার মান -17891602 হয়
ভেরিয়েবল মান 4 হয়


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

Class CPlusPlusJavaFunctionality {
    public:
       CPlusPlusJavaFunctionality(){
         attribute = new int;
         *attribute = value;
       }

       void setValue(int value){
           *attribute = value;
       }

       void getValue(){
          return *attribute;
       }

       ~ CPlusPlusJavaFuncitonality(){
          delete(attribute);
       }

    private:
       int *attribute;
}

void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
   int* prt = obj.attribute;
   *ptr = value;
}

int main(){

   CPlusPlusJavaFunctionality obj;

   obj.setValue(10);

   cout<< obj.getValue();  //output: 10

   changeObjectAttribute(obj, 15);

   cout<< obj.getValue();  //output: 15
}

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







c++-faq