সি ++ স্থানীয় ভেরিয়েবলের কাছে ফিরে আসা রেফারেন্স


116

নীচের কোডটি (ফানক 1 ()) সঠিক হয় যদি এটি i ফেরত দিতে হয়? আমি কোথাও পড়ার কথা মনে করি যে কোনও স্থানীয় ভেরিয়েবলের রেফারেন্স দেওয়ার সময় কোনও সমস্যা আছে। এটি কীভাবে ফানক 2 () থেকে আলাদা?

int& func1()
{
    int i;
    i = 1;
    return i;
}

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

1
আপনি যদি গতিশীলভাবে বরাদ্দ মেমরিটি ব্যবহার করতে ফান 1 () পরিবর্তন করেন তবে সেগুলি একই :-)int& i = * new int;
মার্টিন ইয়র্ক

1
Const স্থানীয়দের জন্য সম্পর্কিত: stackoverflow.com/questions/2784262/...
সিরো Santilli郝海东冠状病六四事件法轮功

উত্তর:


193

এই কোড স্নিপেট:

int& func1()
{
    int i;
    i = 1;
    return i;
}

কাজ করবে না কারণ আপনি ফাংশন কলের আওতায় সীমাবদ্ধ জীবনব্যাপী কোনও বস্তুর কাছে একটি উপনাম (একটি রেফারেন্স) ফিরিয়ে দিচ্ছেন। তার অর্থ একবারে func1()ফিরে আসে, int iমারা যায়, ফাংশন থেকে ফিরে আসা রেফারেন্সকে অর্থহীন কারণ এটি এখন এমন কোনও বস্তুর উল্লেখ করে যা বিদ্যমান নেই।

int main()
{
    int& p = func1();
    /* p is garbage */
}

দ্বিতীয় সংস্করণটি কাজ করে কারণ ভেরিয়েবলটি ফ্রি স্টোরে বরাদ্দ করা হয় যা ফাংশন কলটির আজীবন সীমাবদ্ধ নয়। তবে deleteবরাদ্দ দেওয়ার জন্য আপনি দায়বদ্ধ int

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

int main()
{
    int* p = func2();
    /* pointee still exists */
    delete p; // get rid of it
}

সাধারণত আপনি কিছু আরএআইআই ক্লাস এবং / অথবা একটি কারখানার ফাংশনে পয়েন্টারটি মোড়ানো করবেন যাতে আপনার deleteনিজের এটি নিজেই না থাকে ।

উভয় ক্ষেত্রেই, আপনি কেবল মূল্যটি নিজেই ফিরিয়ে দিতে পারেন (যদিও আমি উপলব্ধি করেছি যে আপনি যে উদাহরণটি দিয়েছেন তা সম্ভবত অনুমোদিত হয়েছে):

int func3()
{
    return 1;
}

int main()
{
    int v = func3();
    // do whatever you want with the returned value
}

নোট করুন যে বড় অবজেক্টগুলি একইভাবে func3()আদিম মানগুলি ফেরত দেওয়া পুরোপুরি ঠিক আছে কারণ আজকাল প্রায় প্রতিটি সংকলক রিটার্ন মান অপ্টিমাইজেশনের কিছু ফর্ম প্রয়োগ করে :

class big_object 
{ 
public:
    big_object(/* constructor arguments */);
    ~big_object();
    big_object(const big_object& rhs);
    big_object& operator=(const big_object& rhs);
    /* public methods */
private:
    /* data members */
};

big_object func4()
{
    return big_object(/* constructor arguments */);
}

int main()
{
     // no copy is actually made, if your compiler supports RVO
    big_object o = func4();    
}

মজার বিষয় হল, সাময়িকভাবে একটি কনস্ট্যান্ড রেফারেন্সকে বাঁধাই পুরোপুরি আইনী সি ++

int main()
{
    // This works! The returned temporary will last as long as the reference exists
    const big_object& o = func4();    
    // This does *not* work! It's not legal C++ because reference is not const.
    // big_object& o = func4();  
}

2
সুন্দর ব্যাখ্যা। : হ্যাটিপ: তৃতীয় কোড স্নিপেটে, আপনি এখন মুছে ফেলছেন, আপনি int* p = func2(); delete p;যখন 'পি' মুছে ফেলেছেন , তার অর্থ কি ফাংশনের func2()সংজ্ঞাটি "ভিতরে" বরাদ্দ করা মেমরিটি মুছে ফেলা হয়েছে?
অ্যাকোরিয়াস_গার্ল

2
@ আনিশা কাউল: হ্যাঁ স্মৃতিটি ভিতরে বরাদ্দ করা হয়েছিল func2()এবং পরের লাইনে বাইরে প্রকাশ করা হয়েছিল । এটি মেমরি হ্যান্ডেল করার পরিবর্তে ত্রুটি-প্রবণ উপায়, যেমন আমি বলেছিলাম যে আপনি এর পরিবর্তে আরএআইআইয়ের কিছু বৈকল্পিক ব্যবহার করবেন। যাইহোক, আপনি সি -+ শিখছেন বলে মনে হচ্ছে। আমি শিখার জন্য একটি ভাল প্রারম্ভিক সি ++ বই তুলে নেওয়ার পরামর্শ দিচ্ছি । এছাড়াও, ভবিষ্যতের রেফারেন্সের জন্য আপনার যদি প্রশ্ন থাকে, আপনি সর্বদা প্রশ্ন স্ট্যাক ওভারফ্লোতে পোস্ট করতে পারেন। মন্তব্যগুলি সম্পূর্ণ নতুন প্রশ্ন জিজ্ঞাসার জন্য নয়।
সিলিকো

এখন আমি বুঝতে পেরেছি, তুমি ঠিক করেছ! ফাংশনটি একটি পয়েন্টারটি ফিরিয়েছিল এবং সেই ফাংশনের বাইরে আপনি যা মেমরিটি দেখিয়েছিলেন তা আপনি মুছে ফেলেছেন। এটি এখন পরিষ্কার, এবং লিঙ্কের জন্য ধন্যবাদ।
অ্যাকোরিয়াস_গর্ল

এবং আপনি উত্তর সম্পাদনা করেছেন ?? : পাগল: আমি সহজেই এটি মিস করতে পারতাম। ;);)
অ্যাকোরিয়াস_গর্ল

@ আনিশা কৌল: না, আমি করিনি। সর্বশেষবার আমি আমার উত্তরটি সম্পাদনা করেছি আমার পোস্টের নীচে থাকা সময় স্ট্যাম্প অনুসারে 10 জানুয়ারী।
সিলিকো

18

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

একবার ফাংশনটি ফিরে আসে এবং শেষ হলেও জিনিসগুলি বিপজ্জনক হয়ে ওঠে। সাধারণত আপনি ফিরে আসার সময় মেমরিটি মুছে ফেলা হয় না বা ওভাররাইট করা হয় না, মানে সেই ঠিকানাগুলিতে থাকা মেমরিটি এখনও আপনার ডেটা ধারণ করে - পয়েন্টারটি বৈধ বলে মনে হয়।

যতক্ষণ না অন্য ফাংশন স্ট্যাকটি আপ করে এবং এটিকে ওভাররাইট করে। এই কারণেই এটি কিছু সময়ের জন্য কাজ করতে পারে - এবং তারপরে হঠাৎ করে বিশেষভাবে গভীরভাবে নেস্ট করা ফাংশনগুলির একটি সেট, বা সত্যিই বিশাল আকারের বা অনেকগুলি স্থানীয় বস্তুর সাথে একটি ফাংশন, সেই স্ট্যাক-স্মৃতিতে আবার পৌঁছায়।

এমনকি এটি একই ঘটতে পারে যে আপনি আবার একই প্রোগ্রাম অংশে পৌঁছেছেন এবং আপনার পুরানো স্থানীয় ফাংশন ভেরিয়েবলটি নতুন ফাংশন ভেরিয়েবলের সাথে ওভাররাইট করে। এগুলি খুব বিপজ্জনক এবং ভারী নিরুৎসাহিত করা উচিত। স্থানীয় বস্তুগুলিতে পয়েন্টার ব্যবহার করবেন না!


2

একটি সহজ জিনিস মনে রাখা এই সাধারণ নিয়মগুলি এবং সেগুলি প্যারামিটার এবং রিটার্ন উভয় প্রকারের ক্ষেত্রেই প্রযোজ্য ...

  • মান - প্রশ্নযুক্ত আইটেমটির একটি অনুলিপি তৈরি করে।
  • পয়েন্টার - প্রশ্নযুক্ত আইটেমের ঠিকানা বোঝায়।
  • রেফারেন্স - আক্ষরিকভাবে প্রশ্নে আইটেম।

প্রত্যেকের জন্য একটি সময় এবং জায়গা রয়েছে, তাই নিশ্চিত হয়ে নিন যে আপনি সেগুলি জানেন। স্থানীয় ভেরিয়েবলগুলি, যেমন আপনি এখানে দেখিয়েছেন, কেবলমাত্র এটিই ফাংশনের সুযোগে স্থানীয়ভাবে বেঁচে থাকার সময় সীমাবদ্ধ। আপনার উদাহরণে রিটার্নের ধরণ থাকা int*এবং ফিরে আসা &iসমানভাবে ভুল হত। আপনি যদি এক্ষেত্রে আরও ভাল হন তবে ...

void func1(int& oValue)
{
    oValue = 1;
}

এটি করা আপনার প্যারামিটারে সরাসরি পাসের মান পরিবর্তন করবে। যদিও এই কোডটি ...

void func1(int oValue)
{
    oValue = 1;
}

হবে না. এটি কেবল oValueফাংশন কলে স্থানীয় মান পরিবর্তন করবে change এর কারণ হ'ল আপনি প্রকৃতপক্ষে কেবল "স্থানীয়" অনুলিপি পরিবর্তন oValueকরছেন oValue

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