C++ এর নীতি লিখার আগে কিছু মৌলিক জ্ঞান থাকা দরকার, কমপক্ষে এই নিয়মগুলি জানা উচিত। নিম্নলিখিত তথ্যের জন্য পুনর্নির্দেশ করুন
যদি কেউ নিজেকে প্রোগ্রামার বলে দাবি করে কিন্তু মেমরি সম্পর্কে কিছুই জানে না, তাহলে আমি আপনাকে বলতে পারি যে সে অবশ্যই গর্বিত। C বা C++ প্রোগ্রাম লেখার জন্য, মেমরির প্রতি আরও বেশি মনোযোগ দেওয়া দরকার, এটি কেবলমাত্র মেমরির সঠিক বরাদ্দ কি না তা সরাসরি প্রোগ্রামের দক্ষতা এবং কর্মক্ষমতাকে প্রভাবিত করে না, তবে আরও গুরুত্বপূর্ণভাবে, যখন আমরা মেমরি পরিচালনা করি তখন সমস্যা দেখা দেয়, এবং অনেক সময়, এই সমস্যাগুলি খুব সহজেই সনাক্ত করা যায় না, যেমন মেমরির ফুটো, যেমন ঝুলন্ত পয়েন্টার। আমি আজ এখানে এই সমস্যাগুলি এড়ানোর বিষয়ে আলোচনা করতে চাই না, তবে সি ++ মেমরি অবজেক্টগুলিকে অন্য দৃষ্টিকোণ থেকে জানার চেষ্টা করছি।
আমরা জানি, C++ মেমরিকে তিনটি লজিক্যাল অঞ্চলে ভাগ করেঃ স্ট্যাক, প্যাড এবং স্ট্যাটিক স্টোরেজ অঞ্চল। এইভাবে, আমি তাদের মধ্যে অবস্থিত বস্তুগুলিকে পৃথকভাবে স্ট্যাক অবজেক্ট, প্যাড অবজেক্ট এবং স্ট্যাটিক অবজেক্ট বলেছি। তাহলে এই বিভিন্ন মেমরি অবজেক্টগুলির মধ্যে পার্থক্য কী? প্যাড অবজেক্ট এবং প্যাড অবজেক্টগুলির মধ্যে কী কী সুবিধা রয়েছে? কীভাবে প্যাড অবজেক্ট বা প্যাড অবজেক্ট তৈরি করা নিষিদ্ধ করা যায়? এগুলি আজকের বিষয়।
১ মৌলিক ধারণা
প্রথমেই দেখে নেওয়া যাক
Type stack_object ;
stack_object হল একটি স্ট্যাক অবজেক্ট, যার জীবনকাল একটি সংজ্ঞা বিন্দু থেকে শুরু হয় এবং যখন তার ফাংশনটি ফিরে আসে তখন তার জীবন শেষ হয়।
উপরন্তু, প্রায় সব অস্থায়ী বস্তুগুলি হ'ল টেমপ্লেট অবজেক্টগুলি; উদাহরণস্বরূপ, নিম্নলিখিত ফাংশন সংজ্ঞাঃ
Type fun(Type object);
এই ফাংশনটি কমপক্ষে দুটি অস্থায়ী বস্তু উৎপন্ন করে, প্রথমত, প্যারামিটারগুলি মান অনুসারে প্রেরণ করা হয়, তাই কপি নির্মাণ ফাংশনটি কল করা হয় যাতে একটি অস্থায়ী বস্তু object_copy1 উৎপন্ন হয়। ফাংশনের অভ্যন্তরে ব্যবহৃত বস্তুটি object_copy1 নয়, বরং object_copy1। স্বাভাবিকভাবেই, object_copy1 একটি প্যাটার্ন অবজেক্ট, যা ফাংশনটি ফিরে আসার সময় মুক্তি পায়; এবং এই ফাংশনটি মান ফেরত দেয়, যখন ফাংশনটি ফিরে আসে, তখন একটি অস্থায়ী বস্তু object_copy2 উত্পন্ন হয়, যদি আমরা রিটার্ন ভ্যালু অপ্টিমাইজেশান (NRV) বিবেচনা না করি তবে অস্থায়ী বস্তুটি ফাংশনটি ফিরে আসার পরে কিছু সময়ের জন্য মুক্তি পায়। উদাহরণস্বরূপ, একটি ফাংশনের নিম্নলিখিত কোড রয়েছেঃ
Type tt ,result ; //生成两个栈对象
tt = fun(tt); //函数返回时,生成的是一个临时对象object_copy2
উপরের দ্বিতীয় বিবৃতির বাস্তবায়নটি হ'ল ফাংশন ফান ফিরে আসার সময় প্রথমে একটি অস্থায়ী অবজেক্ট object_copy2 তৈরি করা হয় এবং তারপরে অ্যাসাইনমেন্ট অপারেটরটি কল করা হয়।
tt = object_copy2 ; //调用赋值运算符
আপনি দেখতে পাচ্ছেন কি? কম্পাইলার আমাদের জন্য অনেক অস্থায়ী বস্তু তৈরি করে, আমাদের কোন ধারণাই না থাকায়, এবং এই অস্থায়ী বস্তু তৈরি করার সময় এবং স্থানের ব্যয় অনেক বেশি হতে পারে, তাই, আপনি হয়তো বুঝতে পারবেন কেন ফাংশন প্যারামিটারগুলিকে ভ্যালু দ্বারা ফাংশন প্যারামিটারগুলির পরিবর্তে কনস্ট রেফারেন্স দিয়ে পাস করা ভাল।
পরবর্তী, স্ট্যাক দেখুন. স্ট্যাক, যাকে ফ্রি স্টোরেজও বলা হয়, এটি প্রোগ্রাম চালানোর সময় গতিশীলভাবে বরাদ্দ করা হয়, তাই এর সবচেয়ে বড় বৈশিষ্ট্য হ'ল গতিশীলতা। সি ++ তে, সমস্ত স্ট্যাক অবজেক্ট তৈরি এবং ধ্বংস করার জন্য প্রোগ্রামার দায়বদ্ধ, তাই এটি যদি খারাপভাবে পরিচালিত হয় তবে মেমরির সমস্যা দেখা দেয়। যদি স্ট্যাক অবজেক্ট বরাদ্দ করা হয় তবে মুক্তি দেওয়া ভুলে যায় তবে মেমরি ফাঁস হয়; এবং যদি কোনও অবজেক্ট মুক্ত করা হয় তবে সংশ্লিষ্ট পয়েন্টারটি NULL এ সেট না করা হয় তবে এই পয়েন্টারটি তথাকথিত ঝুলন্ত পয়েন্টার পয়েন্টার, যা আবার এই পয়েন্টারটি ব্যবহার করার সময় অবৈধ অ্যাক্সেস ঘটে এবং গুরুতরভাবে প্রোগ্রামের ক্র্যাশের কারণ হয়।
তাহলে, সি++ তে স্ট্যাকের বস্তু কিভাবে বরাদ্দ করা হয়? একমাত্র উপায় হল new (অবশ্যই, ম্যালক কমান্ডের মাধ্যমেও সি-প্রকারের স্ট্যাক মেমরি পাওয়া যায়), যা কেবলমাত্র new ব্যবহার করে স্ট্যাকের মধ্যে একটি মেমরি বরাদ্দ করে এবং পয়েন্টারে ফিরে আসে।
আবার স্ট্যাটিক স্টোরেজ অঞ্চলের দিকে ফিরে তাকান; সমস্ত স্ট্যাটিক বস্তু, গ্লোবাল বস্তুগুলিকে স্ট্যাটিক স্টোরেজ অঞ্চলে বরাদ্দ করা হয়; গ্লোবাল অবজেক্টগুলির ক্ষেত্রে, এটি main() ফাংশনটি চালানোর আগে বরাদ্দ করা হয়। প্রকৃতপক্ষে, main() ফাংশনে প্রদর্শিত কোডটি চালানোর আগে, কম্পাইলার দ্বারা উত্পন্ন একটি _main() ফাংশন কল করা হয়, এবং _main() ফাংশনটি সমস্ত গ্লোবাল অবজেক্টের নির্মাণ এবং প্রারম্ভিকীকরণের কাজ করে; এবং main () ফাংশনটি শেষ হওয়ার আগে, কম্পাইলার দ্বারা উত্পন্ন একটি exit ফাংশন কল করা হয়, যা সমস্ত গ্লোবাল অবজেক্টকে মুক্তি দেয়। উদাহরণস্বরূপ, নিম্নলিখিত কোডটিঃ
void main(void)
{
... // 显式代码
}
// 实际上转化为这样:
void main(void)
{
_main(); //隐式代码,由编译器产生,用以构造所有全局对象
... // 显式代码
...
exit() ; // 隐式代码,由编译器产生,用以释放所有全局对象
}
সুতরাং, এটা জানার পর, এর থেকে কিছু কৌশল বের করা যায়, যেমন, যদি আমরা main() ফাংশন চালানোর আগে কিছু প্রস্তুতি কাজ করতে চাই, তাহলে আমরা এই প্রস্তুতি কাজগুলিকে একটি কাস্টমাইজড গ্লোবাল অবজেক্টের কনস্ট্রাক্টর ফাংশনে লিখতে পারি, যাতে main() ফাংশনের এক্সপ্লিক্ট কোড চালানোর আগে, এই গ্লোবাল অবজেক্টের কনস্ট্রাক্টর ফাংশনটি কল করা হয় এবং প্রত্যাশিত ক্রিয়াটি সম্পাদন করে, যা আমাদের লক্ষ্যে পৌঁছে যায়।
আরেকটি স্ট্যাটিক বস্তু হল এটি ক্লাসের একটি স্ট্যাটিক সদস্য।
প্রথম সমস্যাটি হল ক্লাসের স্ট্যাটিক সদস্য বস্তুর জীবনকাল, ক্লাসের স্ট্যাটিক সদস্য বস্তুটি প্রথম ক্লাস অবজেক্টের সৃষ্টির সাথে সাথে উৎপন্ন হয় এবং পুরো প্রোগ্রামের শেষে বিলুপ্ত হয়ে যায়। অর্থাৎ, এমন একটি পরিস্থিতি বিদ্যমান, যেখানে প্রোগ্রামের মধ্যে আমরা একটি ক্লাস সংজ্ঞায়িত করি, যার ক্লাসের সদস্য হিসাবে একটি স্ট্যাটিক বস্তু রয়েছে, কিন্তু প্রোগ্রামের এক্সিকিউশন চলাকালীন, যদি আমরা এই ক্লাস অবজেক্টগুলির মধ্যে একটিও তৈরি না করি, তবে ক্লাসের অন্তর্ভুক্ত স্ট্যাটিক বস্তুটিও তৈরি হবে না। সুতরাং, যদি একাধিক ক্লাস অবজেক্ট তৈরি করা হয়, তবে এই সমস্ত অবজেক্টগুলি একটি স্ট্যাটিক অবজেক্ট সদস্য ভাগ করে নেয়।
দ্বিতীয় প্রশ্নটি হল, যখন নিম্নলিখিত পরিস্থিতির মুখোমুখি হয়ঃ
class Base
{
public:
static Type s_object ;
}
class Derived1 : public Base / / 公共继承
{
... // other data
}
class Derived2 : public Base / / 公共继承
{
... // other data
}
Base example ;
Derivde1 example1 ;
Derivde2 example2 ;
example.s_object = …… ;
example1.s_object = …… ;
example2.s_object = …… ;
উপরের তিনটি blackbody statements-এর উপর নজর দিন, s_object-এর দ্বারা তারা একই বস্তুকে দেখছে? উত্তরটি হল হ্যাঁ, তারা একই বস্তুকে দেখছে, যা সত্য বলে মনে হচ্ছে না, তাই না? কিন্তু এটা সত্য, আপনি নিজের জন্য একটি সহজ কোড লিখতে পারেন এবং এটি যাচাই করতে পারেন। আমি যা করতে যাচ্ছি তা হল কেন এটি ঘটে? আমরা জানি যে যখন একটি Derived1, যেমন Base, অন্য একটি থেকে উত্তরাধিকার সূত্রে আসে, তখন এটি একটি derived1 অবজেক্টের মধ্যে একটি Base টাইপের অবজেক্ট রয়েছে, যা একটি subobject। একটি derived1 অবজেক্টের জন্য একটি বড় মেমরি বিন্যাস রয়েছে যেমনঃ
ধরুন আমরা একটি Derived1 টাইপ অবজেক্টকে একটি ফাংশনকে পাস করি যা Base টাইপ এর অ-উল্লেখ্য প্যারামিটার গ্রহণ করে, তখন কি ভাবে এটি কেটে ফেলা হয়? বিশ্বাস করুন আপনি এখন জানেন যে, এটি শুধুমাত্র Derived1 টাইপ অবজেক্টের Subobject কে বের করে নিয়েছে, এবং Derived1 এর অন্যান্য কাস্টমাইজড ডেটা সদস্যদের উপেক্ষা করে, এবং তারপর এই Subobject কে ফাংশনকে পাস করেছে ((প্রকৃতপক্ষে, ফাংশনটি Subobject এর একটি কপি ব্যবহার করে) ।)
বেস ক্লাসের সকল অবজেক্টের একটি Subobject থাকে (এটি একটি Derived1 অবজেক্টের জন্য একটি বেস পয়েন্টার দিয়ে নির্দেশিত কী, যা স্বাভাবিকভাবেই একটি বহু-স্তরীয় কী) এবং সকল Subobject এবং সকল Base-type অবজেক্টের মধ্যে একই s_object থাকে।
২ তিনটি মেমরি অবজেক্টের তুলনা
টেমপ্লেটগুলির সুবিধা হ'ল এটি যথাযথ সময়ে স্বয়ংক্রিয়ভাবে উত্পন্ন হয় এবং যথাযথ সময়ে স্বয়ংক্রিয়ভাবে ধ্বংস হয়, প্রোগ্রামারদের চিন্তা করার দরকার নেই; এবং টেমপ্লেটগুলি সাধারণত স্ট্যাকের চেয়ে দ্রুত তৈরি হয়, কারণ স্ট্যাকের অবজেক্টগুলি বরাদ্দ করার সময়, অপারেটর নতুন অপারেশনটি কল করা হয়, অপারেটর নতুন কোনও মেমোরি স্পেস অনুসন্ধান অ্যালগরিদম গ্রহণ করে, যা অনুসন্ধান প্রক্রিয়াটি খুব সময়সাপেক্ষ হতে পারে। টেমপ্লেটগুলি উত্পাদন করা এত ঝামেলা নয়, এটি কেবল টেমপ্লেট পয়েন্টারটি সরিয়ে নিতে পারে। তবে এটি লক্ষ করা উচিত যে সাধারণত টেমপ্লেটগুলির ক্ষমতার তুলনামূলকভাবে ছোট, সাধারণত 1 এমবি 2 এমবি, তাই তুলনামূলকভাবে বড় আকারের অবজেক্টগুলি টেমপ্লেট বরাদ্দে উপযুক্ত নয়। বিশেষত রিটার্ন ফাংশনে টেমপ্লেটগুলি ব্যবহার করা ভাল, কারণ রিটার্ন কলের গভীরতা বাড়ার সাথে সাথে সাথে প্রয়োজনীয় টেমপ্লেট স্প
স্ট্যাক অবজেক্ট, যার সৃষ্টি এবং ধ্বংস সময় উভয়ই প্রোগ্রামার-নির্ধারিত, অর্থাৎ প্রোগ্রামারকে স্ট্যাক অবজেক্টের জীবনের উপর সম্পূর্ণ নিয়ন্ত্রণ থাকতে হবে। আমরা প্রায়শই এমন একটি অবজেক্ট চাই, যেমন, আমরা একটি বস্তু তৈরি করতে চাই যা একাধিক ফাংশন দ্বারা অ্যাক্সেস করা যেতে পারে, তবে এটি বিশ্বব্যাপী হতে চাই না, তবে এই সময়ে একটি স্ট্যাক অবজেক্ট তৈরি করা নিঃসন্দেহে একটি ভাল বিকল্প, এবং তারপরে বিভিন্ন ফাংশনের মধ্যে এই স্ট্যাক অবজেক্টের নির্দেশক পাস করা, যাতে এই অবজেক্টটির ভাগ করা সম্ভব হয়। এছাড়াও, স্ট্যাকের ধারণক্ষমতা হ'ল জুমের জায়গার তুলনায় অনেক বেশি। বাস্তবে, যখন শারীরিক মেমরি অপর্যাপ্ত হয়, তখন যদি এই সময়ে একটি নতুন স্ট্যাক অবজেক্ট তৈরি করতে হয়, তখন সাধারণত ত্রুটি হয় না, তবে সিস্টেমটি ভার্চুয়াল মেমরি ব্যবহার করে প্রকৃত শারীরিক মেমরি প্রসারিত করে।
স্ট্যাটিক অবজেক্ট দেখুন।
প্রথমত, গ্লোবাল অবজেক্টগুলো; গ্লোবাল অবজেক্টগুলো ক্লাসের মধ্যে যোগাযোগ এবং ফাংশনের মধ্যে যোগাযোগের জন্য সবচেয়ে সহজ উপায় প্রদান করে, যদিও এই পদ্ধতিটি অত্যাশ্চর্য নয়; সাধারণভাবে, সম্পূর্ণরূপে বস্তুমুখী ভাষায় গ্লোবাল অবজেক্টগুলো নেই, যেমন সি#তে, কারণ গ্লোবাল অবজেক্টগুলো অনিরাপদ এবং উচ্চ সংযোজন বোঝায়, এবং প্রোগ্রামে খুব বেশি গ্লোবাল অবজেক্ট ব্যবহার করা প্রোগ্রামের দৃঢ়তা, স্থিতিশীলতা, রক্ষণাবেক্ষণযোগ্যতা এবং পুনরাবৃত্তিযোগ্যতাকে ব্যাপকভাবে হ্রাস করবে; সি++ও সম্পূর্ণরূপে গ্লোবাল অবজেক্টগুলোকে বাদ দিতে পারে, কিন্তু শেষ পর্যন্ত তা করে না, আমার মনে হয় এর অন্যতম কারণ হল সি-এর সামঞ্জস্যতা।
এরপরে ক্লাসের স্ট্যাটিক সদস্য, যা উপরে উল্লেখ করা হয়েছে, মূল ক্লাস এবং তার বংশোদ্ভূত ক্লাসের সমস্ত অবজেক্ট এই স্ট্যাটিক সদস্য অবজেক্টটি ভাগ করে নেয়, তাই যখন এই ক্লাসগুলির মধ্যে বা এই ক্লাস অবজেক্টগুলির মধ্যে ডেটা ভাগ করে নেওয়া বা যোগাযোগের প্রয়োজন হয় তখন এই ধরনের স্ট্যাটিক সদস্য অবশ্যই একটি ভাল পছন্দ।
তারপর স্ট্যাটিক স্থানীয় বস্তু, যা প্রধানত যে ফাংশনটি অবস্থিত তা আউটপুট কলের সময় মধ্যবর্তী অবস্থা সংরক্ষণ করতে ব্যবহৃত হয়, যার মধ্যে সবচেয়ে উল্লেখযোগ্য উদাহরণ হল রিটার্ন ফাংশন, আমরা জানি যে রিটার্ন ফাংশনটি নিজের ফাংশনটি কল করে, যদি একটি ননস্ট্যাটিক স্থানীয় বস্তুকে রিটার্ন ফাংশনে সংজ্ঞায়িত করা হয়, তবে যখন রিটার্নের সংখ্যা যথেষ্ট হয়, তখন এর ব্যয়ও বিশাল হয়। এটি কারণ ননস্ট্যাটিক স্থানীয় বস্তুগুলি হ'ল চক্রের বস্তু, প্রতিটি আউটপুট কলের সাথে এটি এমন একটি বস্তু তৈরি করে, প্রতিটি প্রত্যাবর্তন কলের সাথে এটি মুক্ত করে, এবং, এই জাতীয় বস্তুগুলি কেবল বর্তমান কল স্তরে সীমাবদ্ধ, আরও গভীর নেস্টার এবং আরও অল্প স্তরের জন্য অদৃশ্য। প্রতিটি স্তরের নিজস্ব স্থানীয় বস্তু এবং পরামিতি রয়েছে।
পুনরাবৃত্তিমূলক ফাংশন ডিজাইনে, স্ট্যাটিক বস্তুগুলিকে ননস্ট্যাটিক স্থানীয় বস্তুগুলির পরিবর্তে ব্যবহার করা যেতে পারে, যা কেবলমাত্র পুনরাবৃত্তিমূলক কল এবং প্রত্যাবর্তনের সময় ননস্ট্যাটিক বস্তুগুলি উত্পাদন এবং মুক্তির ব্যয় হ্রাস করে না, তবে স্ট্যাটিক বস্তুগুলি পুনরাবৃত্তিমূলক কলগুলির মধ্যবর্তী অবস্থা সংরক্ষণ করে এবং বিভিন্ন কল স্তরের জন্য অ্যাক্সেসযোগ্য।
3 অজান্তেই ফসল কাটা
যেমনটি আগে বলা হয়েছে, টেমপ্লেটগুলি যথাসময়ে তৈরি করা হয় এবং যথাসময়ে স্বয়ংক্রিয়ভাবে প্রকাশ করা হয়, অর্থাৎ টেমপ্লেটগুলির স্বয়ংক্রিয় পরিচালনার বৈশিষ্ট্য রয়েছে। তাহলে টেমপ্লেটগুলি কীসের জন্য স্বয়ংক্রিয়ভাবে প্রকাশিত হয়? প্রথমত, তাদের জীবনের শেষের দিকে; দ্বিতীয়ত, যখন তাদের অবস্থানের ফাংশনগুলিতে অস্বাভাবিকতা ঘটে। আপনি বলতে পারেন, এগুলি সবই স্বাভাবিক, কিছুই বড় নয়। হ্যাঁ, কিছুই বড় নয়। তবে যদি আমরা আরও গভীরভাবে যাই তবে সম্ভবত কিছু অপ্রত্যাশিত ফসল রয়েছে।
যদি আমরা স্টেক অবজেক্টে রিসোর্স মুক্ত করার কাজটি করি এবং স্টেক অবজেক্টে রিসোর্স মুক্ত করার কাজটি করি, তবে রিসোর্স লিকের সম্ভাবনা ব্যাপকভাবে হ্রাস পাবে, কারণ স্টেক অবজেক্টগুলি স্বয়ংক্রিয়ভাবে রিসোর্স মুক্ত করতে পারে, এমনকি যখন তাদের ফাংশনটি অস্বাভাবিক হয়। বাস্তব প্রক্রিয়াটি হ'লঃ যখন ফাংশনটি অস্বাভাবিক হয় তখন স্টেক_উনউইন্ডিং ঘটে, অর্থাৎ স্টেকটি খোলা হয়, কারণ স্টেক অবজেক্টগুলি প্রাকৃতিকভাবে স্টেকটিতে উপস্থিত থাকে, তাই স্টেক রিসোর্স মুক্ত করার জন্য স্টেক অবজেক্টের বিশ্লেষণ ফাংশনটি কার্যকর করা হয়। যদি আমরা স্টেক অবজেক্টগুলি ব্যবহার না করি তবে আমরা কেবলমাত্র একটি স্বয়ংক্রিয় বিকল্প তৈরি করতে চাই।
4 স্ট্যাক অবজেক্ট তৈরি করা নিষিদ্ধ
উপরে উল্লিখিত হিসাবে, আপনি যদি একটি নির্দিষ্ট ধরণের স্ট্যাক অবজেক্ট তৈরি করতে নিষেধ করার সিদ্ধান্ত নেন, তবে আপনি নিজেরাই একটি রিসোর্স প্যাকেজ ক্লাস তৈরি করতে পারেন, যা কেবল প্যাকেজটিতে উত্পন্ন হতে পারে, যাতে অস্বাভাবিক পরিস্থিতিতে প্যাকেজযুক্ত সংস্থানগুলি স্বয়ংক্রিয়ভাবে মুক্তি পায়।
তাহলে কিভাবে স্ট্যাক অবজেক্ট তৈরি করতে নিষেধ করা যায়? আমরা ইতিমধ্যে জানি, স্ট্যাক অবজেক্ট তৈরি করার একমাত্র উপায় হল new অপারেশন ব্যবহার করা, যদি আমরা new ব্যবহার করতে নিষেধ করি তাহলে কি তা কার্যকর হবে না. আরও, new অপারেশনটি অপারেটর new কে কল করবে, এবং অপারেটর new পুনরায় লোড করা যাবে. একটি উপায় আছে, যা হল new operator কে private করা, এবং সমান্তরালতার জন্য, অপারেটরকেও private হিসাবে পুনরায় লোড করা ভাল। এখন, আপনি হয়তো আবার প্রশ্ন করতে পারেন, পিল অবজেক্ট তৈরি করার জন্য new কল করার প্রয়োজন নেই? হ্যাঁ, প্রয়োজন নেই, কারণ পিল অবজেক্ট তৈরি করার জন্য মেনু অনুসন্ধান করার প্রয়োজন নেই, বরং সরাসরি পিল পয়েন্টগুলি সামঞ্জস্য করুন, যা পিলের বিরুদ্ধে চাপ দেবে, এবং new অপারেটরের প্রধান কাজটি হ'ল উপযুক্ত স্ট্যাক মেমরি অনুসন্ধান করা, যা স্ট্যাক অবজেক্টের জন্য স্থান বরাদ্দ করবে, যা উপরে উল্লেখ করা হয়েছে। আসুন নিচের কোডের উদাহরণটি দেখিঃ
#include <stdlib.h> //需要用到C式内存分配函数
class Resource ; //代表需要被封装的资源类
class NoHashObject
{
private:
Resource* ptr ;//指向被封装的资源
... ... //其它数据成员
void* operator new(size_t size) //非严格实现,仅作示意之用
{
return malloc(size) ;
}
void operator delete(void* pp) //非严格实现,仅作示意之用
{
free(pp) ;
}
public:
NoHashObject()
{
//此处可以获得需要封装的资源,并让ptr指针指向该资源
ptr = new Resource() ;
}
~NoHashObject()
{
delete ptr ; //释放封装的资源
}
};
NoHashObject এখন একটি বর্গ যা স্ট্যাকের বস্তুগুলিকে নিষিদ্ধ করে, যদি আপনি নিম্নলিখিত কোডটি লিখেনঃ
NoHashObject* fp = new NoHashObject (()) ; // কম্পাইলের সময় ত্রুটি!
fp মুছে ফেলা হবে;
উপরের কোডটি সংকলনের সময় ত্রুটি তৈরি করে। ঠিক আছে, এখন আপনি কীভাবে একটি নিষিদ্ধ স্ট্যাক অবজেক্টের জন্য একটি শ্রেণী ডিজাইন করবেন তা জানেন, আপনি সম্ভবত আমার মতোই প্রশ্ন করেছেন, যদি শ্রেণীর সংজ্ঞা পরিবর্তন করা যায় না তবে কি অবশ্যই এই ধরণের স্ট্যাক অবজেক্ট তৈরি করা যাবে না? না, তবে একটি পদ্ধতি আছে, আমি এটিকে কল করি হিংস্রভাবে ভাঙ্গার ফর্মুলা। সি++ এত শক্তিশালী, শক্তিশালী যে আপনি এটি দিয়ে যা চান তা করতে পারেন। এখানে প্রধান কৌশলটি হ'ল পয়েন্টার টাইপের জোর করে রূপান্তর।
void main(void)
{
char* temp = new char[sizeof(NoHashObject)] ;
//强制类型转换,现在ptr是一个指向NoHashObject对象的指针
NoHashObject* obj_ptr = (NoHashObject*)temp ;
temp = NULL ; //防止通过temp指针修改NoHashObject对象
//再一次强制类型转换,让rp指针指向堆中NoHashObject对象的ptr成员
Resource* rp = (Resource*)obj_ptr ;
//初始化obj_ptr指向的NoHashObject对象的ptr成员
rp = new Resource() ;
//现在可以通过使用obj_ptr指针使用堆中的NoHashObject对象成员了
... ...
delete rp ;//释放资源
temp = (char*)obj_ptr ;
obj_ptr = NULL ;//防止悬挂指针产生
delete [] temp ;//释放NoHashObject对象所占的堆空间。
}
উপরের বাস্তবায়নটি সমস্যাযুক্ত এবং এই বাস্তবায়নটি প্রায়ই ব্যবহার করা হয় না, তবে আমি এখনও একটি উপায় লিখেছি, কারণ এটি বোঝা আমাদের সি ++ মেমরি অবজেক্টগুলি বোঝার জন্য উপকারী। উপরের এতগুলি বাধ্যতামূলক টাইপ রূপান্তরগুলির মধ্যে সবচেয়ে মৌলিক কী? আমরা এটি বুঝতে পারিঃ
মেমরির একটি টুকরো তথ্য অপরিবর্তনীয়, এবং টাইপ হল আমরা যে চশমা পরে থাকি, এবং যখন আমরা একটি চশমা পরে থাকি, তখন আমরা মেমরির ডেটা ব্যাখ্যা করার জন্য একটি সংশ্লিষ্ট টাইপ ব্যবহার করি, যাতে বিভিন্ন ব্যাখ্যা বিভিন্ন তথ্য দেয়।
বাধ্যতামূলক টাইপ রূপান্তর আসলে একই মেমরি ডেটা দেখার জন্য অন্য চশমা প্রতিস্থাপন করে।
এটিও মনে রাখতে হবে যে বিভিন্ন কম্পাইলারের জন্য অবজেক্টের সদস্য ডেটার বিন্যাস ভিন্ন হতে পারে, উদাহরণস্বরূপ, বেশিরভাগ কম্পাইলার NoHashObject এর ptr পয়েন্টার সদস্যকে অবজেক্ট স্পেসের প্রথম 4 বাইটের মধ্যে স্থাপন করে, যাতে নিম্নলিখিত বিবৃতির রূপান্তর কার্যকলাপটি আমরা প্রত্যাশিত হিসাবে কার্যকর হয় তা নিশ্চিত করেঃ
রিসোর্স* rp = (রিসোর্স*) obj_ptr ;
তবে, সব কম্পাইলারদের ক্ষেত্রে তা হয় না।
যদি আমরা এক ধরণের স্ট্যাক অবজেক্ট তৈরি করতে নিষেধ করতে পারি, তাহলে কি আমরা একটি ক্লাস ডিজাইন করতে পারি যাতে এটি একটি টুকরো অবজেক্ট তৈরি করতে পারে না? অবশ্যই।
৫. জিনের বস্তু তৈরি করা নিষিদ্ধ
যেমনটি পূর্বে উল্লেখ করা হয়েছে, একটি টেমপ্লেট তৈরি করার সময়, টেমপ্লেটটির জন্য উপযুক্ত আকারের স্থানটি সরিয়ে নেওয়ার জন্য টেমপ্লেটটির পয়েন্টারটি সরিয়ে নেওয়া হয়, তারপরে এই স্থানে সরাসরি একটি টেমপ্লেট তৈরি করতে সংশ্লিষ্ট কনস্ট্রাকশন ফাংশনটি কল করা হয়, এবং যখন ফাংশনটি ফিরে আসে, তখন এটি তার ডায়ালগ ফাংশনটি কল করে এটিকে মুক্ত করে এবং তারপরে টেমপ্লেটটি পুনরুদ্ধারের জন্য টেমপ্লেটটি পুনরুদ্ধার করে। এই প্রক্রিয়াটিতে অপারেটর নতুন / মুছে ফেলা অপারেশন প্রয়োজন হয় না, তাই অপারেটর নতুন / মুছে ফেলা ব্যক্তিগত হিসাবে সেট করা যায় না। অবশ্যই উপরের বিবরণ থেকে আপনি সম্ভবত ভেবেছিলেনঃ ডায়ালগটি একটি কনস্ট্রাকশন ফাংশন বা একটি কনস্ট্রাকশন ফাংশনকে ব্যক্তিগত হিসাবে সেট করুন, যাতে সিস্টেমটি একটি কনস্ট্রাকশন / ডায়ালগ ফাংশনটি ব্যবহার করতে পারে না এবং অবশ্যই টেমপ্লেট তৈরি করতে পারে না।
এটি সম্ভব, এবং আমি এটি ব্যবহার করতে চাই। তবে এর আগে, একটি বিষয় বিবেচনা করা দরকার, যদি আমরা কাঠামোগত ফাংশনটি ব্যক্তিগত হিসাবে সেট করি, তবে আমরা সরাসরি new দিয়ে স্ট্যাক অবজেক্ট তৈরি করতে পারি না, কারণ new অবজেক্টের জন্য স্থান বরাদ্দ করার পরে তার কাঠামোগত ফাংশনটিও কল করবে। সুতরাং, আমি কেবল ডায়ালগ ফাংশনটিকে ব্যক্তিগত হিসাবে সেট করতে চাই। আরও এগিয়ে, ডায়ালগ ফাংশনটিকে ব্যক্তিগত হিসাবে সেট করা কি কেবল প্যাকেজিং অবজেক্টের উত্পাদনকে সীমাবদ্ধ করবে তা ছাড়াও অন্যান্য প্রভাবও রয়েছে?
যদি একটি শ্রেণীকে বেস শ্রেণী হিসেবে বিবেচনা করা না হয়, তাহলে সাধারণত তার ডায়ালগ ফাংশনকে private হিসেবে ঘোষণা করা হয়।
প্যাকেজ অবজেক্টকে সীমাবদ্ধ করার জন্য, কিন্তু উত্তরাধিকারকে সীমাবদ্ধ না করার জন্য, আমরা প্যাকেজ ফাংশনটিকে protected হিসাবে ঘোষণা করতে পারি, যাতে উভয়ই ভাল হয়; নিম্নলিখিত কোডটি দেখায়ঃ
class NoStackObject
{
protected:
~NoStackObject() { }
public:
void destroy()
{
delete this ;//调用保护析构函数
}
};
তারপর, আপনি NoStackObject ক্লাস ব্যবহার করতে পারেনঃ
NoStackObject* hash_ptr = নতুন NoStackObject() ;
...... // hash_ptr নির্দেশিত বস্তুর উপর অপারেশন
hash_ptr->destroy (()); আমি জানি ওহ, এটা কি একটু অদ্ভুত মনে হচ্ছে, আমরা নতুন দিয়ে একটি বস্তু তৈরি করি, কিন্তু এটি মুছে ফেলার জন্য delete ব্যবহার করি না, বরং এটি ধ্বংস করার পদ্ধতি ব্যবহার করি। স্পষ্টতই, ব্যবহারকারীরা এই অদ্ভুত পদ্ধতিতে অভ্যস্ত নয়। সুতরাং, আমি নির্মাতা ফাংশনটি ব্যক্তিগত বা সুরক্ষিত হিসাবেও সেট করার সিদ্ধান্ত নিয়েছি। এটি আবার উপরে এড়ানোর চেষ্টা করা প্রশ্নের দিকে ফিরে আসে, অর্থাৎ নতুন ব্যবহার না করে একটি বস্তু তৈরি করার উপায় কী? আমরা একটি অপ্রত্যক্ষ পদ্ধতিতে এটি করতে পারি, যা একটি স্ট্যাটিক সদস্য ফাংশন সরবরাহ করে যা এই ধরণের স্ট্যাক অবজেক্ট তৈরির জন্য বিশেষভাবে ব্যবহৃত হয়।
class NoStackObject
{
protected:
NoStackObject() { }
~NoStackObject() { }
public:
static NoStackObject* creatInstance()
{
return new NoStackObject() ;//调用保护的构造函数
}
void destroy()
{
delete this ;//调用保护的析构函数
}
};
এখন আপনি NoStackObject ক্লাসটি এভাবে ব্যবহার করতে পারেনঃ
NoStackObject* hash_ptr = NoStackObject::creatInstance() ;
...... // hash_ptr নির্দেশিত বস্তুর উপর অপারেশন
hash_ptr->destroy() ;
hash_ptr = NULL; // ঝুলন্ত পয়েন্টার ব্যবহার করা প্রতিরোধ করুন
এখন মনে হচ্ছে না যে, বস্তু তৈরি এবং মুক্তির অপারেশন একই রকম।
অনেক সি বা সি++ প্রোগ্রামাররা জাস্ট রিসাইক্লিং নিয়ে নাক গলাতে থাকেন, মনে করেন যে জাস্ট রিসাইক্লিং অবশ্যই গতিশীল মেমরি পরিচালনা করার জন্য নিজের চেয়ে কম কার্যকর হবে এবং রিসাইক্লিংয়ের সময় অবশ্যই প্রোগ্রামটিকে সেখানে থামিয়ে দেবে, এবং যদি তারা মেমরি পরিচালনা নিয়ন্ত্রণ করে তবে বরাদ্দ এবং মুক্তির সময়গুলি স্থিতিশীল হয় এবং প্রোগ্রামটি থামিয়ে দেয় না। অবশেষে, অনেক সি/সি++ প্রোগ্রামার দৃঢ়ভাবে বিশ্বাস করেন যে সি/সি++ এ জাস্ট রিসাইক্লিং প্রক্রিয়া বাস্তবায়ন করা যাবে না। এই ভুল ধারণাগুলি জাস্ট রিসাইক্লিংয়ের অ্যালগরিদমগুলি না বোঝার কারণে অনুমান করা হয়।
প্রকৃতপক্ষে, আবর্জনা পুনর্ব্যবহারের প্রক্রিয়া ধীর নয়, এমনকি গতিশীল মেমরির চেয়েও বেশি কার্যকর। যেহেতু আমরা কেবল বরাদ্দ করতে পারি না, তাই বরাদ্দ মেমরির সময় কেবলমাত্র মজুত থেকে সর্বদা নতুন মেমরি অর্জন করা দরকার, চলমান মজুতের পয়েন্টারগুলি যথেষ্ট; এবং মুক্তি প্রক্রিয়াটি বাদ দেওয়া হয়েছে এবং স্বাভাবিকভাবেই গতি বাড়ানো হয়েছে। আধুনিক আবর্জনা পুনর্ব্যবহারের অ্যালগরিদমগুলি অনেক উন্নতি করেছে এবং ইনক্রিমেন্টাল সংগ্রহের অ্যালগরিদমগুলি ইতিমধ্যে আবর্জনা পুনর্ব্যবহারের প্রক্রিয়াটি ধাপে ধাপে চালিত করতে সক্ষম হয়েছে, যা প্রক্রিয়াটি বাধাগ্রস্ত করা এড়ায়। traditionalতিহ্যবাহী গতিশীল মেমরি পরিচালনার অ্যালগরিদমগুলিও যথাযথ সময়ে মেমরির টুকরা সংগ্রহের কাজটি করে, আবর্জনা পুনর্ব্যবহারের চেয়ে বেশি সুবিধাজনক নয়।
জাঙ্ক রিসাইক্লিং এর ভিত্তি সাধারণত স্কেন করা এবং বর্তমানে ব্যবহার করা যেতে পারে এমন সমস্ত মেমরি ব্লক চিহ্নিত করা এবং ইতিমধ্যে বরাদ্দ করা সমস্ত মেমরি থেকে চিহ্নিত করা মেমরি পুনরুদ্ধার করা হয়। C/C++ এ জাঙ্ক রিসাইক্লিং করা অসম্ভব ধারণাটি সাধারণত সঠিকভাবে ব্যবহার করা যেতে পারে এমন সমস্ত মেমরি ব্লক স্ক্যান করা অসম্ভব বলে মনে করা হয়, তবে যা অসম্ভব বলে মনে হয় তা বাস্তবে বাস্তবায়িত হয় না। প্রথমত, স্কেন করা মেমরির ডেটা দিয়ে, স্ট্যাকের দিকে গতিশীলভাবে বরাদ্দ করা মেমরির পয়েন্টগুলি সহজেই চিহ্নিত করা যায়, যদি কোনও সনাক্তকরণ ত্রুটি থাকে তবে কেবলমাত্র কিছু পয়েন্টার নয় এমন ডেটা পয়েন্টার হিসাবে চিহ্নিত করা যায়, যা পয়েন্টারগুলিকে অ-পয়েন্টার ডেটা হিসাবে চিহ্নিত করে না। এইভাবে, জাঙ্ক রিসাইক্লিং প্রক্রিয়াটি কেবলমাত্র ভুলগুলি পুনরুদ্ধার করে এবং ভুলগুলি পরিষ্কার করে না এমন মেমরি ফাংশনগুলি পুনরুদ্ধার করে। যদি
ট্র্যাশ পুনর্ব্যবহারের সময়, কেবলমাত্র bss বিভাগ, ডেটা বিভাগ এবং বর্তমানে ব্যবহৃত কয়েন স্পেসটি স্ক্যান করে একটি সম্ভাব্য গতিশীল মেমরি পয়েন্টারের পরিমাণ খুঁজে পাওয়া যায়।
আপনার প্রকল্পের জন্য যদি আপনি একটি ভাল ট্র্যাশ রিসাইকেল বাস্তবায়ন করতে চান তবে মেমরি পরিচালনার গতি বাড়ানো বা এমনকি মোট মেমরি খরচ হ্রাস করা সম্ভব। যদি আপনি আগ্রহী হন তবে ট্র্যাশ রিসাইক্লিং সম্পর্কিত অনলাইনে থাকা নিবন্ধগুলি এবং বাস্তবায়িত লাইব্রেরিগুলি অনুসন্ধান করুন।
পুনর্নির্দেশিতএইচ কে ঝাং
#include<stdio.h>
int*fun(){
int k = 12;
return &k;
}
int main(){
int *p = fun();
printf("%d\n", *p);
getchar();
return 0;
}
এটি কেবল অ্যাক্সেসযোগ্য নয়, এটি পরিবর্তনযোগ্য, তবে এই অ্যাক্সেসটি অনিশ্চিত। স্থানীয় ভেরিয়েবলের ঠিকানাগুলি প্রোগ্রামের নিজস্ব স্ট্যাকের মধ্যে রয়েছে, এবং কর্তৃপক্ষের ভেরিয়েবলটি শেষ হওয়ার পরে, স্থানীয় ভেরিয়েবলের মেমরি ঠিকানাটি অন্য ভেরিয়েবলের কাছে না দেওয়া হলে এর মানটি বিদ্যমান থাকবে। তবে এটি পরিবর্তন করা আরও বিপজ্জনক, কারণ এই মেমরি ঠিকানাটি প্রোগ্রামের অন্যান্য ভেরিয়েবলকে দেওয়া হতে পারে, যা পয়েন্টার দ্বারা জোর করে পরিবর্তন করা হলে প্রোগ্রামটি ক্র্যাশ হতে পারে।