C ++ کی حکمت عملی لکھنے سے پہلے کچھ بنیادی معلومات جاننے کی ضرورت ہے ، کم از کم ان قواعد کو جاننا ضروری ہے۔ ذیل میں نقل کے لئے ہے:
اگر کوئی شخص خود کو پروگرامر سمجھتا ہے لیکن میموری کے بارے میں کچھ نہیں جانتا ہے تو میں آپ کو بتا سکتا ہوں کہ وہ ضرور فخر کر رہا ہے۔ C یا C++ میں پروگرام لکھنے کے ل memory ، میموری پر زیادہ توجہ دینے کی ضرورت ہے ، نہ صرف اس لئے کہ میموری کی مناسب تقسیم کا براہ راست پروگرام کی کارکردگی اور کارکردگی پر براہ راست اثر پڑتا ہے ، بلکہ اس سے بھی اہم بات یہ ہے کہ جب ہم میموری کا استعمال کرتے ہیں تو ہم غلطی سے پریشانیوں کا سامنا کرتے ہیں ، اور بہت سے معاملات میں ، یہ پریشانیاں آسانی سے معلوم نہیں ہوتی ہیں ، جیسے میموری لیک ، جیسے ہینگ پوائنٹرز۔ آج میں یہاں ان مسائل سے بچنے کے بارے میں بات نہیں کرنا چاہتا ہوں ، بلکہ ایک اور نقطہ نظر سے C ++ میموری آبجیکٹ کو جاننے کے بارے میں بات کرنا چاہتا ہوں۔
ہم جانتے ہیں کہ C++ میموری کو تین منطقی علاقوں میں تقسیم کرتا ہے: اسٹیک، ہیک اور جامد اسٹوریج۔ اس کے بعد، میں ان میں واقع اشیاء کو اسٹیک، ہیک اور جامد اشیاء کے طور پر بیان کرتا ہوں۔ تو پھر ان مختلف میموری اشیاء میں کیا فرق ہے؟ اسٹیک اور ہیک کے مابین کیا فوائد اور نقصانات ہیں؟ اسٹیک یا ہیک کو بنانے سے کیسے منع کیا جائے؟ یہ آج کا موضوع ہے۔
1 بنیادی تصورات
سب سے پہلے ، آئیے دیکھتے ہیں
Type stack_object ;
stack_object ایک اسٹیک آبجیکٹ ہے جس کی زندگی اسٹیکنگ پوائنٹ سے شروع ہوتی ہے اور اس کی زندگی اس وقت ختم ہوتی ہے جب اس کا فنکشن واپس آجاتا ہے۔
مزید برآں، تقریبا تمام عارضی اشیاء شیشے کی اشیاء ہیں۔ مثال کے طور پر، مندرجہ ذیل فنکشن کی تعریف:
Type fun(Type object);
یہ فنکشن کم از کم دو عارضی اشیاء پیدا کرتا ہے، سب سے پہلے، پیرامیٹرز قدر کے مطابق منتقل کیے جاتے ہیں، لہذا کاپی بنانے والے فنکشن کو کال کرنے سے ایک عارضی اعتراض object_copy1 پیدا ہوتا ہے، جس کا استعمال فنکشن کے اندر کیا جاتا ہے، نہ کہ object_copy1 بلکہ object_copy1۔ قدرتی طور پر، object_copy1 ایک شیشے کا اعتراض ہے، جو فنکشن کی واپسی پر جاری کیا جاتا ہے۔ اور یہ فنکشن قدر کی واپسی ہے، جب فنکشن واپس آتا ہے، تو یہ بھی ایک عارضی اعتراض object_copy2 پیدا کرتا ہے، اگر ہم واپسی کی قیمت کو بہتر بنانے کے بارے میں غور نہیں کرتے ہیں، تو یہ عارضی اعتراض فنکشن کی واپسی کے بعد کچھ وقت کے لئے جاری کیا جاتا ہے۔ مثال کے طور پر، کسی فنکشن میں یہ کوڈ ہے:
Type tt ,result ; //生成两个栈对象
tt = fun(tt); //函数返回时,生成的是一个临时对象object_copy2
مندرجہ بالا دوسرے بیان کا نفاذ اس طرح ہے کہ جب فنکشن fun واپس آتا ہے تو پہلے ایک عارضی اعتراض object_copy2 تیار کیا جاتا ہے ، اور پھر تفویض کرنے والے آپریٹر کو کال کیا جاتا ہے۔
tt = object_copy2 ; //调用赋值运算符
کیا آپ نے دیکھا؟ کمپائلر نے ہمارے لئے بہت سے عارضی اشیاء تیار کیں ، جس کے بارے میں ہم نہیں جانتے تھے ، اور ان عارضی اشیاء کو تیار کرنے میں وقت اور جگہ کی لاگت بہت زیادہ ہوسکتی ہے ، لہذا ، آپ شاید سمجھ سکتے ہیں کہ کنسٹ حوالہ جات کے بجائے فنکشن کے پیرامیٹرز کو قدر کے مطابق منتقل کرنے کے بجائے کنسٹ حوالہ جات کے ساتھ منتقل کرنا کیوں بہتر ہے۔
اس کے بعد ، اسٹیک کو دیکھو۔ اسٹیک ، جسے آزاد اسٹوریج زون بھی کہا جاتا ہے ، پروگرام کے عملدرآمد کے دوران متحرک طور پر مختص کیا جاتا ہے ، لہذا اس کی سب سے بڑی خصوصیت متحرک ہے۔ سی ++ میں ، اسٹیک کے تمام اشیاء کی تخلیق اور تباہی کا ذمہ دار پروگرامر ہے ، لہذا ، اگر اسے غلط طریقے سے سنبھالا جائے تو ، میموری کے مسائل پیدا ہوسکتے ہیں۔ اگر اسٹیک کے اعتراضات مختص کیے گئے ہیں ، لیکن اسے جاری کرنا بھول گئے ہیں تو ، میموری لیک ہوجاتا ہے۔ اور اگر کسی اعتراض کو آزاد کردیا گیا ہے ، لیکن اس کے مطابق پوائنٹر کو NULL میں نہیں رکھا گیا ہے تو ، یہ پوائنٹر نام نہاد پھنسے ہوئے پوائنٹر ہے ، جو اس پوائنٹر کو دوبارہ استعمال کرنے پر غیر قانونی رسائی کا سبب بنتا ہے ، جس سے پروگرام گرنے کا سبب بنتا ہے۔
تو ، C ++ میں اسٹیک آبجیکٹ کو کیسے مختص کیا جاتا ہے؟ واحد طریقہ یہ ہے کہ نئے کا استعمال کریں (یقینا ، مالوک کی طرح کی ہدایات بھی سی کی طرح اسٹیک میموری حاصل کرسکتے ہیں) ، صرف نئے کا استعمال کرتے ہوئے ، اسٹیک میں ایک ٹکڑا میموری مختص کی جائے گی ، اور اس چیز کی طرف اشارہ کرنے والے اشارے کو واپس کردیا جائے گا۔
اسٹیٹک اسٹوریج زون کی طرف واپس جائیں۔ تمام جامد اشیاء، گلوبل اشیاء کو جامد اسٹوریج زون میں تفویض کیا جاتا ہے۔ گلوبل اشیاء کے بارے میں، یہ سب سے پہلے اہم () کے عمل کو انجام دینے سے پہلے ہی تفویض کیا جاتا ہے۔ دراصل، اہم () کے عمل میں دکھائے جانے والے کوڈ کو انجام دینے سے پہلے ، کمپائلر کے ذریعہ تیار کردہ ایک اہم () فنکشن کو بلایا جاتا ہے ، جبکہ اہم () فنکشن تمام گلوبل اشیاء کی تعمیر اور ابتدائیہ کا کام کرتا ہے۔ اور اہم () فنکشن کے اختتام سے پہلے ، کمپائلر کے ذریعہ تیار کردہ ایکزٹ فنکشن کو بلایا جاتا ہے ، تاکہ تمام گلوبل اشیاء کو آزاد کیا جاسکے۔ مثال کے طور پر ، مندرجہ ذیل کوڈ:
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 = …… ;
براہ کرم نوٹ کریں کہ مندرجہ بالا تین جملے جو بلیک باڈیز کے طور پر نشان زد ہیں ، کیا وہ ایک ہی چیز ہیں جس کا s_object ان کا دورہ کرتا ہے؟ جواب ہاں میں ہے ، وہ واقعی ایک ہی چیز کی طرف اشارہ کرتے ہیں ، جو سچ کی طرح نہیں لگتا ہے ، ہے نا؟ لیکن یہ سچ ہے ، آپ اپنے آپ کو ایک سادہ کوڈ لکھ سکتے ہیں اور اس کی تصدیق کرسکتے ہیں۔ میں یہ کیوں کروں گا؟ ہم جانتے ہیں کہ جب ایک قسم جیسے ڈیریویڈ 1 کسی اور قسم جیسے بیس سے وراثت کرتا ہے تو ، اسے ایک ڈیریویڈ 1 اعتراض کے طور پر دیکھا جاسکتا ہے جس میں ایک بیس قسم کا اعتراض ہوتا ہے ، جو ایک سب آبجیکٹ ہے۔
آئیے یہ سوچیں کہ جب ہم کسی Derived1 قسم کے اعتراض کو کسی ایسے فنکشن میں منتقل کرتے ہیں جو بیس قسم کے غیر حوالہ دینے والے پیرامیٹرز کو قبول کرتا ہے تو کس طرح کاٹتا ہے؟ یقین ہے کہ اب آپ جانتے ہیں ، یہ صرف Derived1 قسم کے اعتراض میں موجود سبجیکٹ کو ہٹا دیتا ہے ، اور Derived1 کی اپنی مرضی کے مطابق تمام دوسرے ڈیٹا ممبروں کو نظرانداز کرتا ہے ، اور پھر اس سبجیکٹ کو فنکشن میں منتقل کرتا ہے ((دراصل ، فنکشن اس سبجیکٹ کی کاپی استعمال کرتا ہے)) ۔
بیس کلاس کے تمام آبجیکٹوں میں بیس قسم کا ایک سب آبجیکٹ ہوتا ہے (یہ بیس قسم کے اشارے کے ذریعہ کسی ڈیریویڈ 1 آبجیکٹ کی طرف اشارہ کرنے کے قابل کلید ہے ، جو قدرتی طور پر ایک کثیر مقصدی کلید بھی ہے) ، جبکہ تمام سب آبجیکٹ اور تمام بیس قسم کے آبجیکٹ ایک ہی s_object آبجیکٹ کا اشتراک کرتے ہیں ، قدرتی طور پر ، بیس کلاس سے ماخوذ کلاس کے پورے نظام میں کلاس کی مثالیں ایک ہی s_object آبجیکٹ کا اشتراک کرتی ہیں۔ مندرجہ بالا مثال ، مثال 1 ، مثال 2 کے لئے آبجیکٹ کی ترتیب مندرجہ ذیل ہے:
2 تین قسم کی میموری اشیاء کا موازنہ
ہیک آبجیکٹ کا فائدہ یہ ہے کہ وہ مناسب وقت پر خود بخود پیدا ہوتے ہیں اور مناسب وقت پر خود بخود تباہ ہوجاتے ہیں ، پروگرامر کی فکر کرنے کی ضرورت نہیں ہوتی ہے۔ اور ہیک آبجیکٹ کی تخلیق عام طور پر اسٹیک آبجیکٹ سے تیز ہوتی ہے ، کیونکہ اسٹیک آبجیکٹ کی تقسیم کے وقت ، آپریٹر نیو آپریشن کو بلایا جاتا ہے ، آپریٹر نیو کسی قسم کے میموری اسپیس سرچ الگورتھم کا استعمال کرتا ہے ، اور یہ تلاش کا عمل بہت وقت طلب ہوسکتا ہے ، اور ہیک آبجیکٹ تیار کرنا اتنا پریشانی نہیں ہوتا ہے ، اس کے لئے صرف ہلکے ہیک اشارے کی ضرورت ہوتی ہے۔ تاہم ، نوٹ کریں کہ عام طور پر ہیک اسپیس کی گنجائش نسبتا small چھوٹی ہوتی ہے ، عام طور پر 1MB 2MB ، لہذا بڑے سائز کے آبجیکٹ ہیک میں تقسیم کے لئے موزوں نہیں ہوتے ہیں۔ خاص طور پر ریگولیٹری افعال میں ، یہ بہتر ہے کہ ہیک آبجیکٹ کا استعمال نہ کریں ، کیونکہ ریگولیٹری کال کی گہرائی میں اضافے کے ساتھ ہی مطلوبہ ہیک اسپیس میں بھی اضافہ ہوتا ہے
اسٹیک آبجیکٹ، جس کی تخلیق اور تباہی کا وقت پروگرامر کے لئے مخصوص ہے، یعنی پروگرامر اس کی زندگی پر مکمل کنٹرول رکھتا ہے۔ ہم اکثر اس طرح کے آبجیکٹ کی ضرورت رکھتے ہیں۔ مثال کے طور پر، ہم ایک ایسا آبجیکٹ بنانا چاہتے ہیں جس تک متعدد افعال رسائی حاصل کرسکیں ، لیکن ہم اسے عالمی نہیں بنانا چاہتے ہیں۔ اس وقت اسٹیک آبجیکٹ بنانا یقینی طور پر ایک اچھا انتخاب ہے ، اور پھر مختلف افعال کے مابین اس اسٹیک آبجیکٹ کے اشارے کو منتقل کرنا ، تاکہ اس آبجیکٹ کا اشتراک ممکن ہو۔ نیز ، اسٹیک کی گنجائش زیادہ ہے جب کہ اس کی جگہ کی جگہ کی نسبت ہے۔ در حقیقت ، جب جسمانی میموری کافی نہیں ہوتی ہے تو ، اگر اس وقت بھی نیا اسٹیک آبجیکٹ بنانا پڑتا ہے تو ، عام طور پر غلطی نہیں ہوتی ہے ، لیکن نظام مجازی میموری کا استعمال کرتا ہے تاکہ اصل جسمانی میموری کو بڑھا سکے۔
اس کے بعد، static objects پر نظر ڈالیں۔
سب سے پہلے ، گلوبل آبجیکٹ۔ گلوبل آبجیکٹ کلاسوں کے مابین اور افعال کے مابین مواصلات کا ایک آسان ترین طریقہ فراہم کرتا ہے ، اگرچہ یہ طریقہ خوبصورت نہیں ہے۔ عام طور پر ، مکمل طور پر آبجیکٹ پر مبنی زبانوں میں ، گلوبل آبجیکٹ موجود نہیں ہوتے ہیں ، جیسے C # ، کیونکہ گلوبل آبجیکٹ کا مطلب غیر محفوظ اور اعلی وابستگی ہوتا ہے ، اور پروگرام میں بہت زیادہ گلوبل آبجیکٹ کا استعمال پروگرام کی استحکام ، استحکام ، بحالی اور دوبارہ استعمال میں بہت کم پڑتا ہے۔ C ++ بھی گلوبل آبجیکٹ کو مکمل طور پر ختم کرسکتا ہے ، لیکن آخر کار نہیں ، میں سمجھتا ہوں کہ ایک وجہ یہ ہے کہ ہم آہنگ C کے لئے۔
اس کے بعد کلاس کا ایک جامد رکن ہوتا ہے۔ جیسا کہ پہلے ذکر کیا گیا ہے ، بنیادی کلاس اور اس کے مشتق کلاس کے تمام اشیاء اس جامد رکن کا اشتراک کرتے ہیں ، لہذا جب ان کلاسوں کے مابین یا ان کلاس اشیاء کے مابین ڈیٹا شیئرنگ یا مواصلات کی ضرورت ہوتی ہے تو ، اس طرح کے جامد ممبران یقینی طور پر ایک اچھا انتخاب ہیں۔
اس کے بعد ایک جامد مقامی اعتراض ہے، جو بنیادی طور پر اس اعتراض کے واقع ہونے والے فنکشن کو بار بار بلانے کے دوران درمیانی حالت میں رکھنے کے لئے استعمال کیا جاتا ہے۔ اس کی ایک نمایاں مثال ایک بار بار چلنے والا فنکشن ہے۔ ہم سب جانتے ہیں کہ ایک بار بار چلنے والا فنکشن اپنے آپ کو بلاتا ہے ، اگر ایک غیر جامد مقامی اعتراض کو ایک بار بار چلنے والے فنکشن میں بیان کیا جائے تو ، اس کی قیمت بھی بہت زیادہ ہوتی ہے۔ چونکہ غیر جامد مقامی اعتراض ایک شیشے کا اعتراض ہے ، ہر بار بار چلنے پر ، اس طرح کا ایک اعتراض پیدا ہوتا ہے ، ہر بار واپس آنے پر ، یہ اعتراض جاری ہوجاتا ہے ، اور ، اس طرح کے اعتراضات صرف موجودہ کال پرت تک ہی محدود ہیں ، جو زیادہ گہری نچلی تہوں اور زیادہ ہلکی تہوں کے لئے پوشیدہ ہیں۔ ہر سطح کے اپنے مقامی اعتراضات اور پیرامیٹرز ہوتے ہیں۔
ریکوریشن فنکشن ڈیزائن میں ، غیر جامد مقامی اشیاء (جیسے ہیک اشیاء) کی جگہ جامد اشیاء استعمال کی جاسکتی ہیں ، جو نہ صرف ہر ریکوریشن کال اور واپسی پر غیر جامد اشیاء کی تخلیق اور رہائی کے اخراجات کو کم کرتی ہے ، بلکہ جامد اشیاء بھی ریکوریشن کال کے درمیان کی حالت کو محفوظ کرتی ہیں اور ہر کال لیئر کے لئے قابل رسائی ہوتی ہیں۔
3 چکنائی کے استعمال سے غیر متوقع فصل
جیسا کہ پہلے بتایا گیا ہے کہ شیشے کی اشیاء مناسب وقت پر بنائی جاتی ہیں اور پھر مناسب وقت پر خود بخود جاری کی جاتی ہیں، یعنی شیشے کی اشیاء میں خود کار طریقے سے انتظامیہ کی خصوصیات ہوتی ہیں۔ تو شیشے کی اشیاء خود بخود کہاں جاری ہوجاتی ہیں؟ پہلا، جب اس کی زندگی کا اختتام ہوتا ہے؛ دوسرا، جب اس کے اندر موجود افعال میں کوئی غیر معمولی بات ہوتی ہے۔ آپ کہہ سکتے ہیں، یہ سب ٹھیک ہے، کوئی بڑی بات نہیں ہے۔ ہاں، کوئی بڑی بات نہیں ہے۔ لیکن اگر ہم تھوڑا سا گہرا گہرا کرتے ہیں تو، شاید غیر متوقع فصلیں ہیں۔
اگر ہم ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک ہی وقت میں ایک
4 اسٹیک آبجیکٹ کی تخلیق پر پابندی
جیسا کہ اوپر ذکر کیا گیا ہے، اگر آپ نے کسی قسم کے اسٹیک آبجیکٹ کی تخلیق پر پابندی عائد کرنے کا فیصلہ کیا ہے تو، آپ اپنے آپ کو ایک وسائل کا احاطہ کرنے والی کلاس بنا سکتے ہیں، جو صرف ایک ہیج میں پیدا ہوسکتا ہے، تاکہ غیر معمولی حالات میں خود بخود وسائل کو آزاد کیا جا سکے.
تو پھر کس طرح اسٹیک آبجیکٹ بنانے پر پابندی لگائی جائے؟ ہم پہلے ہی جانتے ہیں کہ اسٹیک آبجیکٹ بنانے کا واحد طریقہ نیا استعمال کرنا ہے، اگر ہم نیا استعمال کرنے پر پابندی لگاتے ہیں تو کیا یہ کام نہیں کرے گا۔ مزید برآں، نیا آپریشن آپریشن پر عملدرآمد کرتے وقت آپریٹر نیا کو کال کرتا ہے، جبکہ آپریٹر نیا دوبارہ لوڈ کرنے کے قابل ہے۔ ایک طریقہ یہ ہے کہ نیا آپریٹر نجی بنایا جائے، اور ہم آہنگی کے لئے، آپریٹر کو بھی نجی میں دوبارہ لوڈ کرنا بہتر ہے۔ اب، آپ کو شک ہو سکتا ہے، کیا شیشے کو حذف کرنے کے لئے شیشے کو کال کرنے کی ضرورت نہیں ہے؟ جی ہاں، ضرورت نہیں، کیونکہ شیشے کو بنانے کے لئے میموری کو تلاش کرنے کی ضرورت نہیں ہے، بلکہ براہ راست اسٹیک کی نشاندہی کو ایڈجسٹ کریں، اس کے خلاف پریشر کریں، اور تلاش کرنے کے لئے آپریٹر نیا کا بنیادی کام مناسب شیشے کی میموری کو تلاش کرنا ہے، جس میں اسٹیک آبجیکٹ کے لئے جگہ مختص کرنا ہے، جو اوپر ذکر کیا گیا ہے۔ چلو مندرجہ ذیل کو
#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 حذف کریں ؛
مندرجہ بالا کوڈ مرتب کی مدت میں غلطیاں پیدا کرے گا۔ ٹھیک ہے ، اب جب آپ جانتے ہیں کہ اس قسم کے اسٹیک کو روکنے کے لئے کس طرح ڈیزائن کیا جائے گا ، آپ کو شاید میرے جیسے ہی سوالات ہوں گے ، کیا اس قسم کے اسٹیک کو پیدا نہیں کیا جاسکتا ہے جب کلاس NoHashObject کی تعریف تبدیل نہیں کی جاسکتی ہے؟ نہیں ، یا اس کا کوئی طریقہ ہے ، جس کو میں نے کہا ہے زبانی تشدد کو توڑنے کا طریقہ۔ C ++ اتنا طاقتور ہے کہ آپ اس کے ساتھ کچھ بھی کرسکتے ہیں جو آپ کرنا چاہتے ہیں۔ یہاں بنیادی طور پر استعمال کی جانے والی تکنیک پوائنٹر ٹائپ کی زبردستی تبدیلی ہے۔
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对象所占的堆空间。
}
مندرجہ بالا عملدرآمد پریشان کن ہے، اور یہ عملدرآمد عملی طور پر بہت کم استعمال ہوتا ہے، لیکن میں نے اس کا راستہ لکھا ہے کیونکہ اس کو سمجھنا ہمارے لئے C++ میموری آبجیکٹ کو سمجھنے کے لئے فائدہ مند ہے۔ مندرجہ بالا بہت سے مجبور قسم کے تبادلوں کے لئے بنیادی کیا ہے؟ ہم اس طرح سمجھ سکتے ہیں:
میموری کے ایک ٹکڑے میں موجود اعداد و شمار مستقل ہوتے ہیں اور قسم ہمارے پہنے ہوئے شیشے کی ہوتی ہے۔ جب ہم ایک شیشہ پہنتے ہیں تو ہم میموری میں موجود اعداد و شمار کی تشریح کے لئے اسی قسم کا استعمال کرتے ہیں تاکہ مختلف تشریحات سے مختلف معلومات ملیں۔
جبری قسم کی تبدیلی کا مطلب یہ ہے کہ آپ نے ایک اور عینک کو تبدیل کر دیا ہے اور پھر اسی میموری ڈیٹا کو دوبارہ دیکھنا ہے۔
یہ بھی یاد رکھنا چاہیے کہ مختلف کمپائلرز کے پاس آبجیکٹ کے ممبر ڈیٹا کی ترتیب مختلف ہوسکتی ہے۔ مثال کے طور پر ، زیادہ تر کمپائلرز NoHashObject کے ptr پوائنٹر ممبر کو آبجیکٹ کی جگہ کے پہلے 4 بائٹس میں ترتیب دیتے ہیں تاکہ اس بات کا یقین کیا جاسکے کہ مندرجہ ذیل بیان کا تبادلہ عمل ہم توقع کرتے ہیں:
وسائل* rp = (وسائل*) obj_ptr ؛
تاہم، یہ ضروری نہیں کہ تمام کمپائلرز ایسے ہی ہوں۔
چونکہ ہم کسی قسم کے اسٹیک آبجیکٹ کی پیداوار پر پابندی لگا سکتے ہیں، کیا ہم ایک کلاس کو ڈیزائن کر سکتے ہیں تاکہ اس میں ہیک آبجیکٹ پیدا نہ ہو؟ یقیناً۔
5 ہائیڈرولک اشیاء کی پیداوار پر پابندی
جیسا کہ پہلے ذکر کیا گیا ہے، جب ایک شیشے کا اعتراض تخلیق کیا جاتا ہے تو اس کے لئے مناسب سائز کی جگہ کو ہٹانے کے لئے ڈومین پوائنٹر کو منتقل کیا جاتا ہے، اور اس جگہ پر براہ راست ایک شیشے کا اعتراض بنانے کے لئے ایک تعمیراتی فنکشن کو بلایا جاتا ہے، اور جب یہ کام واپس آتا ہے تو، اس کا تجزیہ کرنے والا فنکشن اس اعتراض کو آزاد کرتا ہے، اور پھر اس شیشے کو تبدیل کرنے کے لئے اس شیشے کی یادداشت کو واپس لے لیتا ہے. اس عمل میں آپریٹر نیا / حذف کرنے کی ضرورت نہیں ہے، لہذا آپریٹر نیا / حذف کرنے کے لئے نجی سیٹ کرنے کا مقصد حاصل نہیں کیا جا سکتا. یقینا آپ نے مندرجہ بالا بیان سے سوچا ہوگا: تجزیہ کرنے کے لئے ایک تعمیراتی فنکشن یا تعمیراتی فنکشن نجی طور پر مقرر کیا جاتا ہے، لہذا نظام کو تعمیراتی / حذف کرنے کے لئے استعمال نہیں کیا جا سکتا، اور یقینا آپ شیشے کی تخلیق میں نہیں جا سکتے.
یہ ممکن ہے، اور میں بھی اس کا استعمال کرنے کا ارادہ رکھتا ہوں۔ لیکن اس سے پہلے، ایک چیز کو واضح کرنے کی ضرورت ہے، یہ ہے کہ اگر ہم تعمیراتی فنکشن کو نجی طور پر مقرر کرتے ہیں، تو ہم نئے کو براہ راست اسٹیک آبجیکٹ پیدا کرنے کے لئے استعمال نہیں کر سکتے ہیں، کیونکہ نیا اس کی تعمیراتی فنکشن کو بھی اس کی جگہ مختص کرنے کے بعد کال کرتا ہے۔ لہذا، میں صرف تجزیاتی فنکشن کو نجی کے طور پر مقرر کرنے کا ارادہ رکھتا ہوں.
اگر کسی کلاس کا بنیادی طبقہ بننے کا ارادہ نہیں ہے تو ، عام طور پر اس کا حل اس کے تجزیہ کار کو نجی قرار دینا ہے۔
اگر آپ کے پاس ایک ہی چیز ہے جو آپ کے پاس نہیں ہے تو ، آپ کو اس کا استعمال کرنے کی ضرورت نہیں ہے۔
class NoStackObject
{
protected:
~NoStackObject() { }
public:
void destroy()
{
delete this ;//调用保护析构函数
}
};
اس کے بعد ، آپ NoStackObject کلاس کو اس طرح استعمال کرسکتے ہیں:
NoStackObject* hash_ptr = نیا NoStackObject() ؛
...... // hash_ptr اشارے پر عملدرآمد
hash_ptr->destroy (() ؛ کیا آپ جانتے ہیں؟ اوہ، کیا یہ عجیب نہیں لگتا ہے کہ ہم ایک اعتراض کو نئے کے ساتھ تخلیق کرتے ہیں، لیکن اسے حذف کرنے کے لئے حذف کرنے کے بجائے اسے تباہ کرنے کے لئے استعمال کرتے ہیں. ظاہر ہے، صارفین کو اس طرح کے عجیب استعمال کے لئے استعمال نہیں کیا جاتا ہے. لہذا، میں نے تعمیراتی افعال کو نجی یا محفوظ کرنے کا فیصلہ کیا. یہ اوپر سے بچنے کی کوشش کی گئی ہے کہ سوال پر واپس آتا ہے.
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 ؛ // ہنگ پوائنٹر استعمال کرنے سے روکتا ہے
اب کیا یہ بہتر محسوس ہوتا ہے؟ اب ایک ہی چیز پیدا کرنے اور ایک ہی چیز کو جاری کرنے کا عمل ہے۔
بہت سے سی یا سی ++ پروگرامرز کو ردی کی ٹوکری کو گھورتے ہیں ، یہ سوچتے ہیں کہ ردی کی ٹوکری کو متحرک میموری کو سنبھالنے کے لئے خود سے کم موثر ہونا ضروری ہے ، اور جب وہ دوبارہ استعمال کرتے ہیں تو وہ یقینی طور پر پروگرام کو روک دیتے ہیں ، اور اگر وہ میموری مینجمنٹ پر قابو رکھتے ہیں تو ، تقسیم اور رہائی کا وقت مستحکم ہوتا ہے ، جس سے پروگرام رک جاتا ہے۔ آخر میں ، بہت سے سی / سی ++ پروگرامر اس بات پر یقین رکھتے ہیں کہ سی / سی ++ میں ردی کی ٹوکری کے طریقہ کار کو لاگو نہیں کیا جاسکتا ہے۔ یہ غلط خیالات ان گندم کو دوبارہ استعمال کرنے کے الگورتھم کو نہ سمجھنے کی وجہ سے قیاس آرائیں۔
حقیقت میں ردی کی ٹوکری کا طریقہ کار سست نہیں ہے اور یہاں تک کہ متحرک میموری کی تقسیم سے بھی زیادہ موثر ہے۔ چونکہ ہم صرف تقسیم نہیں کرسکتے ہیں ، لہذا تقسیم شدہ میموری میں صرف اس وقت کی ضرورت ہوتی ہے جب ڈھیر سے مستقل طور پر نئی میموری حاصل کی جاسکتی ہے ، اس کے لئے ڈھیر کو منتقل کرنے کے اشارے کافی ہیں۔ اور جاری کرنے کے عمل کو خارج کردیا گیا ہے ، اور قدرتی طور پر اس کی رفتار تیز ہوگئی ہے۔ جدید ردی کی ٹوکری کے الگورتھم بہت ترقی کر چکے ہیں ، اضافہ جمع کرنے والے الگورتھم پہلے ہی ردی کی ٹوکری کے عمل کو مرحلہ وار کرنے کی اجازت دیتے ہیں ، جس سے روکنے سے بچنے کے لئے۔ جبکہ روایتی متحرک میموری مینجمنٹ کے الگورتھم بھی مناسب وقت پر میموری کے ٹکڑوں کو جمع کرنے کا کام کرتے ہیں ، اور ردی کی ٹوکری کی بازیافت سے زیادہ فائدہ مند نہیں ہیں۔
جب کہ ردی کی ٹوکری کی وصولی کے لئے الگورتھم کی بنیاد عام طور پر اسکیننگ اور نشان لگانے پر مبنی ہوتی ہے ، جو میموری کے تمام بلاکس کو اس وقت استعمال کیا جاسکتا ہے ، اور پہلے ہی مختص کردہ تمام میموری سے غیر نشان زد شدہ میموری کی وصولی کے لئے۔ C / C ++ میں ، ردی کی ٹوکری کی وصولی کا تصور عام طور پر اس بات پر مبنی ہوتا ہے کہ ممکن نہیں ہے کہ تمام میموری بلاکس کو صحیح طریقے سے اسکین کیا جاسکے جو ابھی بھی استعمال ہوسکتے ہیں ، لیکن ، جو کچھ بھی ناممکن لگتا ہے وہ حقیقت میں قابل عمل نہیں ہے۔ سب سے پہلے ، میموری کو اسکین کرنے سے ، اسٹیک پر متحرک طور پر مختص کردہ میموری کی طرف اشارہ کرنے والے اشارے کو آسانی سے پہچانا جاسکتا ہے ، اور اگر شناخت میں کوئی غلطی ہو تو ، صرف کچھ غیر اشارے والے اعداد و شمار کو اشارے کے طور پر استعمال کیا جاسکتا ہے ، اور اشارے کو غیر اشارے والے اعداد و شمار کے طور پر استعمال نہیں کیا جاسکتا ہے۔ اس طرح ، ردی کی ٹوکری کی وصولی کے عمل میں صرف
ردی کی ٹوکری میں ، صرف بی ایس ایس سیکشن ، ڈیٹا سیکشن ، اور اس وقت استعمال ہونے والی ٹن کی جگہ کو اسکین کرنے کی ضرورت ہے تاکہ متحرک میموری اشارے کی مقدار معلوم کی جاسکے ، اور اس کا حوالہ دیتے ہوئے میموری کی بازگشت اسکین کرنے سے تمام متحرک میموری حاصل ہوسکتی ہے جو فی الحال استعمال ہورہی ہے۔
اگر آپ اپنے پروجیکٹ کے لئے ایک اچھا ردی کی ٹوکری بنانے کے خواہاں ہیں تو ، میموری مینجمنٹ کی رفتار کو بہتر بنانا ، یا یہاں تک کہ مجموعی طور پر میموری کی کھپت کو کم کرنا ممکن ہے۔ اگر آپ دلچسپی رکھتے ہیں تو ، آپ کو ویب پر پہلے سے موجود مضامین اور ری سائیکلنگ کے بارے میں لاگو شدہ لائبریریوں کو تلاش کرنا چاہئے۔
کا ترجمہایچ کے ژانگ
#include<stdio.h>
int*fun(){
int k = 12;
return &k;
}
int main(){
int *p = fun();
printf("%d\n", *p);
getchar();
return 0;
}
یہ نہ صرف قابل رسائی ہے بلکہ اس میں ترمیم بھی کی جا سکتی ہے، لیکن یہ غیر یقینی ہے۔ مقامی متغیرات کے پتے پروگرام کے اپنے اسٹیک میں ہوتے ہیں، اور اتھارٹی متغیر کے اختتام کے بعد، جب تک کہ اس مقامی متغیر کے میموری ایڈریس کو کسی دوسرے متغیر کو نہیں دیا جاتا ہے، اس کی قدر باقی رہتی ہے۔ لیکن اگر اس میں ترمیم کی جاتی ہے تو یہ زیادہ خطرناک ہے، کیونکہ یہ میموری ایڈریس پروگرام کے دوسرے متغیرات کو دیا جا سکتا ہے، جس میں اشارے کے ذریعے زبردستی ترمیم کی جا سکتی ہے، جس سے پروگرام کو حادثہ ہوسکتا ہے.