'নতুন' ব্যবহারের কারণে মেমরি ফাঁস হয় কেন?


131

আমি প্রথমে সি # শিখেছি এবং এখন আমি সি ++ দিয়ে শুরু করছি। আমি যেমন বুঝতে পেরেছি, অপারেটরnew সি ++ এ সি # এর সাথে মিল নেই।

আপনি এই নমুনা কোডটিতে মেমরি ফুটো হওয়ার কারণ ব্যাখ্যা করতে পারেন?

class A { ... };
struct B { ... };

A *object1 = new A();
B object2 = *(new B());

উত্তর:


464

কি হচ্ছে

আপনি যখন লিখবেন T t;আপনি স্বয়ংক্রিয় স্টোরেজ সময়কালT সহ ধরণের একটি অবজেক্ট তৈরি করছেন । সুযোগের বাইরে গেলে এটি স্বয়ংক্রিয়ভাবে পরিষ্কার হয়ে যাবে।

আপনি যখন লিখবেন new T()আপনি গতিশীল স্টোরেজ সময়কালT সহ ধরণের একটি অবজেক্ট তৈরি করছেন । এটি স্বয়ংক্রিয়ভাবে পরিষ্কার হবে না।

পরিষ্কার ছাড়াই নতুন

এটি deleteপরিষ্কার করার জন্য আপনাকে এটিতে একটি পয়েন্টার দিতে হবে :

মোছার সাথে নতুন

যাইহোক, আপনার দ্বিতীয় উদাহরণটি আরও খারাপ: আপনি পয়েন্টারটিকে অবহেলা করছেন এবং অবজেক্টের একটি অনুলিপি করছেন। এভাবে আপনি তৈরি করা অবজেক্টটির পয়েন্টারটি হারাবেন new, তাই আপনি চাইলেও কখনও এটি মুছতে পারবেন না!

ডেরেফের সাথে নতুন

আপনার কি করা উচিত

আপনার স্বয়ংক্রিয় স্টোরেজ সময়কাল পছন্দ করা উচিত। একটি নতুন অবজেক্ট দরকার, কেবল লিখুন:

A a; // a new object of type A
B b; // a new object of type B

আপনার যদি ডায়নামিক স্টোরেজ সময়কাল প্রয়োজন হয় তবে একটি স্বয়ংক্রিয় স্টোরেজ সময়কাল অবজেক্টে বরাদ্দ হওয়া অবজেক্টে পয়েন্টারটি সংরক্ষণ করুন যা এটি স্বয়ংক্রিয়ভাবে মোছা হয়।

template <typename T>
class automatic_pointer {
public:
    automatic_pointer(T* pointer) : pointer(pointer) {}

    // destructor: gets called upon cleanup
    // in this case, we want to use delete
    ~automatic_pointer() { delete pointer; }

    // emulate pointers!
    // with this we can write *p
    T& operator*() const { return *pointer; }
    // and with this we can write p->f()
    T* operator->() const { return pointer; }

private:
    T* pointer;

    // for this example, I'll just forbid copies
    // a smarter class could deal with this some other way
    automatic_pointer(automatic_pointer const&);
    automatic_pointer& operator=(automatic_pointer const&);
};

automatic_pointer<A> a(new A()); // acts like a pointer, but deletes automatically
automatic_pointer<B> b(new B()); // acts like a pointer, but deletes automatically

অটোমেটিক_পয়েন্টার দিয়ে নতুন করা হচ্ছে

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

এই automatic_pointerজিনিসটি ইতিমধ্যে বিভিন্ন রূপে বিদ্যমান, আমি একটি উদাহরণ দেওয়ার জন্য এটি সরবরাহ করেছি। নামক স্ট্যান্ডার্ড লাইব্রেরিতে একটি খুব অনুরূপ শ্রেণীর উপস্থিত রয়েছে std::unique_ptr

এখানে একটি পুরানো নাম (প্রাক-সি ++ 11) রয়েছে auto_ptr তবে এটি এখন অবচয় করা হয়েছে কারণ এটির একটি অদ্ভুত অনুলিপি আচরণ রয়েছে।

এবং তারপরে আরও কিছু স্মার্ট উদাহরণ রয়েছে, যেমন std::shared_ptr, যা একই বস্তুতে একাধিক পয়েন্টারকে অনুমতি দেয় এবং শেষ পয়েন্টারটি ধ্বংস হয়ে গেলে কেবল এটি পরিষ্কার করে।


4
@ ব্যবহারকারী 1131997: আপনি এটিকে আরও একটি প্রশ্ন করেছেন বলে খুশি। আপনি দেখতে পাচ্ছেন মন্তব্যগুলিতে এটি ব্যাখ্যা করা খুব সহজ নয় :)
আর মার্টিনহো ফার্নান্দেস

@ আর.মার্টিনহো ফার্নান্দেস: দুর্দান্ত উত্তর। শুধু একটা প্রশ্ন. অপারেটর * () ফাংশনে আপনি কেন রেফারেন্স ব্যবহার করলেন?
ধ্বংসকারী

@ পরিচালক দেরী উত্তর: ডি। রেফারেন্স দিয়ে ফিরে আসা আপনাকে পয়েন্টটি সংশোধন করতে দেয়, যাতে আপনি যেমন করতে চান যেমন *p += 2কোনও সাধারণ পয়েন্টার থাকে। যদি এটি রেফারেন্স দিয়ে ফিরে না আসে তবে এটি কোনও সাধারণ পয়েন্টারের আচরণ অনুকরণ করে না, যা এখানে উদ্দেশ্য।
আর মার্টিনহো ফার্নান্দেস

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

35

ধাপে ধাপে ব্যাখ্যা:

// creates a new object on the heap:
new B()
// dereferences the object
*(new B())
// calls the copy constructor of B on the object
B object2 = *(new B());

সুতরাং এর শেষে, আপনার গাদা একটি অবজেক্ট রয়েছে যার কোনও পয়েন্টার নেই, সুতরাং মোছা অসম্ভব।

অন্যান্য নমুনা:

A *object1 = new A();

আপনি যদি deleteবরাদ্দ মেমরিটি ভুলে যান তবেই একটি মেমরি ফাঁস হয় :

delete object1;

সি ++ তে স্বয়ংক্রিয় স্টোরেজযুক্ত বস্তু রয়েছে, স্ট্যাকের উপর তৈরি হওয়াগুলি, যা স্বয়ংক্রিয়ভাবে নিষ্পত্তি হয় এবং গতিতে গতিশীল স্টোরেজযুক্ত বস্তুগুলি, যা আপনি বরাদ্দ করেছেন newএবং এটি দিয়ে নিজেকে মুক্ত করতে হবেdelete । (এটি মোটামুটি বলা হয়)

মনে করুন যে deleteবরাদ্দকৃত প্রতিটি বস্তুর জন্য আপনার একটি হওয়া উচিত new

সম্পাদনা

এটি ভাবতে আসুন, object2মেমরি ফুটো হতে হবে না।

নিম্নলিখিত কোডটি কেবল একটি পয়েন্ট দেওয়ার জন্য, এটি একটি খারাপ ধারণা, এর মতো কোডটি কখনও পছন্দ করবেন না:

class B
{
public:
    B() {};   //default constructor
    B(const B& other) //copy constructor, this will be called
                      //on the line B object2 = *(new B())
    {
        delete &other;
    }
}

এই ক্ষেত্রে, যেহেতু otherরেফারেন্স দ্বারা পাস করা হয়, এটি সঠিক পয়েন্টটি দ্বারা নির্দেশিত হবে new B()। সুতরাং, দ্বারা তার ঠিকানা পেয়ে&other এবং পয়েন্টারটি মোছার ফলে স্মৃতিশক্তি মুক্ত হবে।

তবে আমি এটি যথেষ্ট চাপ দিতে পারি না, এটি করবেন না। এটি এখানে একটি বিষয় বলতে।


2
আমি একই ভাবছিলাম: আমরা ফাঁস না হওয়ার জন্য এটি হ্যাক করতে পারি তবে আপনি এটি করতে চাইবেন না। অবজেক্ট 1 এর কোনওটি ফাঁস হওয়ার দরকার নেই, কারণ এর নির্মাতা নিজেকে কোনও ধরণের ডেটা স্ট্রাকচারের সাথে সংযুক্ত করতে পারে যা এটি কোনও সময়ে মুছে ফেলবে।
ক্যাশকু

2
"সবসময় এটি করা সম্ভব তবে উত্তর" দেয় না সেগুলি লেখার জন্য এটি সবসময়ই প্রলুব্ধ হয়! :-) আমি অনুভূতিটি জানি
কোস

11

দুটি "অবজেক্ট" দেওয়া:

obj a;
obj b;

তারা মেমরিতে একই অবস্থান দখল করবে না। অন্য কথায়,&a != &b

অন্যের কাছে একটির মান নির্ধারণ করা তাদের অবস্থান পরিবর্তন করবে না, তবে এটি তাদের সামগ্রীগুলি পরিবর্তন করবে:

obj a;
obj b = a;
//a == b, but &a != &b

স্বজ্ঞাতভাবে, পয়েন্টার "অবজেক্টস" একইভাবে কাজ করে:

obj *a;
obj *b = a;
//a == b, but &a != &b

এখন, আসুন আপনার উদাহরণটি দেখুন:

A *object1 = new A();

এই মান নির্ধারণের হয় new A()থেকে object1। মানটি একটি পয়েন্টার, অর্থ object1 == new A(), তবে &object1 != &(new A())। (দ্রষ্টব্য যে এই উদাহরণটি বৈধ কোড নয়, এটি কেবল ব্যাখ্যার জন্য)

যেহেতু পয়েন্টারের মান সংরক্ষণ করা হয়েছে, আমরা যে স্মৃতিটি দেখিয়েছি তা মুক্ত করতে পারি: delete object1;আমাদের নিয়মের কারণে, এটি একই রকম আচরণ করে delete (new A());যার কোনও ফুটো নেই।


আপনার দ্বিতীয় উদাহরণ হিসাবে, আপনি পয়েন্ট-টু অবজেক্টটি অনুলিপি করছেন। মান হ'ল সেই বস্তুর বিষয়বস্তু, আসল পয়েন্টার নয়। অন্যান্য প্রতিটি ক্ষেত্রে যেমন &object2 != &*(new A()),।

B object2 = *(new B());

আমরা বরাদ্দ মেমরিটির পয়েন্টারটি হারিয়ে ফেলেছি এবং সুতরাং আমরা এটিকে মুক্ত করতে পারি না। delete &object2;এটি কাজ করবে বলে মনে হতে পারে তবে &object2 != &*(new A())এটি সমতুল্য নয় delete (new A())এবং তাই অবৈধ।


9

সি # এবং জাভাতে, আপনি যে কোনও শ্রেণীর উদাহরণ তৈরি করতে নতুন ব্যবহার করেন এবং পরে এটি ধ্বংস করার বিষয়ে আপনার চিন্তা করার দরকার নেই।

সি ++ এর একটি কীওয়ার্ড "নতুন" রয়েছে যা একটি অবজেক্ট তৈরি করে তবে জাভা বা সি # এর বিপরীতে এটি কেবলমাত্র কোনও অবজেক্ট তৈরির উপায় নয়।

সি ++ এর একটি অবজেক্ট তৈরির জন্য দুটি পদ্ধতি রয়েছে:

  • স্বয়ংক্রিয়
  • প্রগতিশীল

স্বয়ংক্রিয়ভাবে তৈরির সাহায্যে আপনি বস্তুটিকে একটি স্কোপযুক্ত পরিবেশে তৈরি করেন: - কোনও ফাংশনে বা - শ্রেণির সদস্য (বা স্ট্রাক্ট) হিসাবে।

কোনও ফাংশনে আপনি এটি এইভাবে তৈরি করবেন:

int func()
{
   A a;
   B b( 1, 2 );
}

কোনও শ্রেণীর মধ্যে আপনি সাধারণত এটিকে এটি তৈরি করতে পারেন:

class A
{
  B b;
public:
  A();
};    

A::A() :
 b( 1, 2 )
{
}

প্রথম ক্ষেত্রে, স্কোপ ব্লকটি প্রস্থান করা হলে অবজেক্টগুলি স্বয়ংক্রিয়ভাবে ধ্বংস হয়ে যায়। এটি কোনও ফাংশনের মধ্যে কোনও ফাংশন বা স্কোপ-ব্লক হতে পারে।

পরবর্তী ক্ষেত্রে অবজেক্ট বি এর সদস্য হওয়া ক এর উদাহরণ সহ একসাথে ধ্বংস হয়ে যায়।

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

এরকম একটি অবজেক্ট হ'ল একটি শেয়ারড_পিটার যা "মুছে ফেলা" যুক্তি সঞ্চার করবে কিন্তু কেবল তখনই যখন শেয়ার্ড_প্টর যে সমস্ত বস্তু ভাগ করে নিচ্ছে তার সমস্ত ঘটনাগুলি ধ্বংস হয়ে যায়।

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

আপনার ধ্বংসকারীদেরও কখনও ব্যতিক্রম ছোঁড়া উচিত নয়।

আপনি যদি এটি করেন তবে আপনার মেমরি ফাঁস হবে।


4
সেখানে বেশী automaticএবং dynamic। এছাড়াও আছে static
মাকিং হাঁস

9
B object2 = *(new B());

এই লাইনটি ফাঁসের কারণ। এর কিছুটা আলাদা করা যাক ..

অবজেক্ট 2 হ'ল বি টাইপের একটি পরিবর্তনশীল, ঠিকানা ঠিকানা 1 এ সঞ্চিত (হ্যাঁ, আমি এখানে স্বেচ্ছাচারিত সংখ্যাগুলি তুলছি)। ডানদিকে, আপনি একটি নতুন বি, বা বি টাইপ করার জন্য একটি পয়েন্টার চেয়েছিলেন প্রোগ্রামটি আপনাকে আনন্দের সাথে এটি দেয় এবং আপনার নতুন বিটিকে 2 ঠিকানার জন্য নির্ধারিত করে এবং 3 ঠিকানায় একটি পয়েন্টারও তৈরি করে Now এখন, ঠিকানা 2 তে ডেটা অ্যাক্সেসের একমাত্র উপায় 3 পয়েন্টারটির মাধ্যমে Next* পয়েন্টারটি নির্দেশ করছে যে ডেটা (ঠিকানা 2 তে থাকা ডেটা) নির্দেশ করছে সেগুলি পেতে আপনি । এটি কার্যকরভাবে সেই ডেটাটির একটি অনুলিপি তৈরি করে এবং ঠিকানার 1 এ নির্ধারিত অবজেক্ট 2-তে এই নিয়োগ করে। মনে রাখবেন, এটি একটি কপি, আসল নয়।

এখন, সমস্যাটি এখানে:

আপনি কখনই যে পয়েন্টারটি ব্যবহার করতে পারবেন তা আসলে সংরক্ষণ করেননি! এই অ্যাসাইনমেন্টটি শেষ হয়ে গেলে, পয়েন্টারটি (ঠিকানা 3 এ থাকা মেমরি, যা আপনি ঠিকানা 2 অ্যাক্সেস করতে ব্যবহার করেছিলেন) সুযোগের বাইরে এবং আপনার নাগালের বাইরে! আপনি এটিতে আর কল মুছতে পারবেন না এবং তাই ঠিকানা 2 এ মেমরিটি পরিষ্কার করতে পারবেন না। আপনি যা রেখে গেছেন তা হ'ল ঠিকানা 1 এ ঠিকানা 2 থেকে ডেটার একটি অনুলিপি। স্মৃতিতে বসে একই জিনিস দুটি। একটি আপনি অ্যাক্সেস করতে পারবেন, অন্যটি আপনি পারবেন না (কারণ আপনি এটির পথটি হারিয়েছেন)। এ কারণেই এটি একটি স্মৃতি ফাঁস।

আমি আপনার সি # ব্যাকগ্রাউন্ড থেকে আসার পরামর্শ দিচ্ছি যে আপনি সি ++ পয়েন্টার কীভাবে কাজ করেন সে সম্পর্কে আপনি অনেক বেশি পড়বেন। এগুলি একটি উন্নত বিষয় এবং বুঝতে কিছুটা সময় নিতে পারে তবে তাদের ব্যবহার আপনার পক্ষে অমূল্য হবে।


8

যদি এটি আরও সহজ করে তোলে, কম্পিউটার মেমোরিটিকে হোটেলের মতো মনে করুন এবং প্রোগ্রামগুলি হ'ল গ্রাহকরা যখন তাদের প্রয়োজন হয় তখন কক্ষ ভাড়া রাখেন।

এই হোটেলটি যেভাবে কাজ করে তা হ'ল আপনি একটি রুম বুকিং করে এবং যখন যাবেন তখন পোর্টারকে জানান।

আপনি যদি কোনও ঘরে বইয়ের প্রোগ্রাম করেন এবং কুলিকে না জানিয়ে পোর্টারটি মনে করেন যে ঘরটি এখনও ব্যবহারযোগ্য এবং অন্য কাউকে এটি ব্যবহার করতে দেবে না। এক্ষেত্রে একটি রুম ফাঁস রয়েছে।

যদি আপনার প্রোগ্রাম মেমরি বরাদ্দ করে এবং এটি মুছে না ফেলে (এটি কেবল এটি ব্যবহার করা বন্ধ করে) তবে কম্পিউটার মনে করে যে মেমরিটি এখনও ব্যবহার রয়েছে এবং অন্য কাউকে এটি ব্যবহার করার অনুমতি দেবে না। এটি একটি স্মৃতি ফাঁস।

এটি কোনও সঠিক উপমা নয় তবে এটি সাহায্য করতে পারে।


5
আমি সেই উপমাটি বেশ পছন্দ করি, এটি নিখুঁত নয়, তবে এটি অবশ্যই নতুন যারা তাদের কাছে স্মৃতি ফাঁসের ব্যাখ্যা দেওয়ার একটি ভাল উপায়!
আদমম

1
লন্ডনের ব্লুমবার্গের একজন সিনিয়র ইঞ্জিনিয়ারের জন্য একটি এইচআর মেয়েকে স্মৃতি স্মৃতি ফাঁস করার জন্য একটি সাক্ষাত্কারে আমি এটি ব্যবহার করেছি। আমি সেই সাক্ষাত্কারটি পেয়েছি কারণ আমি কোনও নন প্রোগ্রামারকে সে বোঝার উপায়ে মেমরি লিক (এবং থ্রেডিংয়ের বিষয়গুলি) আসলে ব্যাখ্যা করতে পেরেছিলাম able
স্টিফান

7

তৈরি করার সময় object2আপনি নতুন দিয়ে তৈরি করা অবজেক্টের একটি অনুলিপি তৈরি করছেন, তবে আপনি (কখনই বরাদ্দ করা হয়নি) পয়েন্টারটি হারাচ্ছেন (সুতরাং এটি মুছে ফেলার কোনও উপায় নেই)। এড়াতে, আপনাকে object2একটি রেফারেন্স তৈরি করতে হবে ।


3
কোনও অবজেক্ট মুছতে রেফারেন্সের ঠিকানা নেওয়া অবিশ্বাস্যরকম খারাপ অভ্যাস। একটি স্মার্ট পয়েন্টার ব্যবহার করুন।
টম হুইটক

3
অবিশ্বাস্যভাবে খারাপ অভ্যাস, তাই না? আপনার কী মনে হয় স্মার্ট পয়েন্টারগুলি পর্দার আড়ালে ব্যবহার করে?
অন্ধ

3
@ ব্লিন্ডির স্মার্ট পয়েন্টার (কমপক্ষে শালীনভাবে প্রয়োগ করা) সরাসরি পয়েন্টার ব্যবহার করে।
লুচিয়ান গ্রিগোর

2
ঠিক আছে, পুরোপুরি সত্যি বলতে, পুরো আইডিয়াটি দুর্দান্ত নয়, তাই না? আসলে, আমি এখনও নিশ্চিত নই যে ওপিতে চেষ্টা করা প্যাটার্নটি আসলে কার্যকর হবে।
মারিও

7

ঠিক আছে, আপনি যদি কোনও মুহুর্তে newঅপারেটরের কাছে সেই মেমরিটির কোনও পয়েন্টারটি অপারেটরের মাধ্যমে অপারেটরটি ব্যবহার করে বরাদ্দ করা মেমরি মুক্ত না করেন তবে আপনি একটি মেমরি ফাঁস তৈরি করেন delete

আপনার উপরের দুটি ক্ষেত্রে:

A *object1 = new A();

এখানে আপনি deleteমেমরিটি মুক্ত করতে ব্যবহার করছেন না , সুতরাং এবং যখন আপনার object1পয়েন্টারটি সুযোগের বাইরে চলে যায় তখন আপনার একটি মেমরি ফাঁস হবে কারণ আপনি পয়েন্টারটি হারিয়ে ফেলেছেন এবং তাই এতে deleteঅপারেটরটি ব্যবহার করতে পারবেন না ।

এবং এখানে

B object2 = *(new B());

আপনি যে পয়েন্টারটি দিয়েছিলেন তা ফিরিয়ে দিচ্ছেন new B(), এবং deleteস্মৃতি মুক্ত হওয়ার জন্য কখনই সেই পয়েন্টারটি পাস করতে পারবেন না । অতএব অন্য একটি স্মৃতি ফুটো।


7

এটি এই লাইনটি অবিলম্বে ফাঁস হচ্ছে:

B object2 = *(new B());

এখানে আপনি Bস্তূপে একটি নতুন অবজেক্ট তৈরি করছেন, তারপরে স্ট্যাকের একটি অনুলিপি তৈরি করছেন। স্তূপে বরাদ্দকৃত একটিকে আর অ্যাক্সেস করা যায় না এবং তাই ফুটো।

এই লাইনটি অবিলম্বে ফাঁস হয় না:

A *object1 = new A();

একটা লিক হতে যদি আপনি না হবে deleteobject1যদিও।


4
গতিশীল / স্বয়ংক্রিয় স্টোরেজ ব্যাখ্যা করার সময় দয়া করে হিপ / স্ট্যাক ব্যবহার করবেন না।
পাব্বি

2
@ পবি কেন ব্যবহার করবেন না? গতিশীল / স্বয়ংচালিত স্টোরেজ কারণে সর্বদা গাদা, স্ট্যাক নয়? এবং এজন্যই স্ট্যাক / হিপ সম্পর্কে বিস্তারিত জানার দরকার নেই, আমি ঠিক আছি?

4
@ user1131997 গাদা / স্ট্যাক বাস্তবায়নের বিশদ। এগুলি সম্পর্কে তারা জেনে রাখা গুরুত্বপূর্ণ তবে এই প্রশ্নের সাথে অপ্রাসঙ্গিক।
পাব্বি

2
হুম আমি এটির একটি পৃথক উত্তর চাই, যেমন আমার মতো তবে heগল / স্ট্যাকটি প্রতিস্থাপন করে যা আপনি সবচেয়ে ভাল বলে মনে করেন। আপনি কীভাবে এটি ব্যাখ্যা করতে পছন্দ করবেন তা জানতে আগ্রহী।
ম্যাটজগ্লোলোয়
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.