استفاده از اشاره گرها در Object Pascal (دلفی)

اشاره گرها یک نوع داده هستند مثل Integer،Char و همانند اینها که در واقع آدرسهای حافظه را ذخیره میکنند. مساله از این جهت مهم است که هر چیزی که استفاده میکنیم حتما یک محل دارد و بنابراین راه دسترسی به آن همین اشاره گرها هستند. ساده؛ اشاره گر به یک رشته (String) اشاره میکند، وقتی از یک شئ (Object) استفاده میکنیم یا حتی زمانی که یک رویداد (Event) را صدا میزنیم، همه و همه اشاره ای به یک اشاره گر هستند. فرض کنید میخواهیم در صفحه نمایش ایجاد افکتهای گرافیکی کنیم، این کار از طریق نوشتن مقادیر مورد نظر در نقاط صفحه نمایش (Pixels) امکان پذیر است، پس کافی است اشاره گری به اولین نقطه داشته باشیم و کل نقاط در اختیار ما هستند.

البته اشاره گرها در دلفی آنگونه که در زبانهای C و C++ مورد توجه هستند، استفاده نمیشود، خصوصا به این دلیل که در Object Pascal سعی شده تا حد امکان از درگیری مستقیم برنامه نویس با اشاره گرها جلوگیری شود.

 

2. چرا به اشاره گرها نیاز داریم؟

خوب! واقعا من به عنوان یک برنامه نویس چه نیازی به دانستن محل حافظه یک متغییر دارم!؟

یک جا نوشته بود: "اشاره گرها میتوانند به هر چیزی اشاره کنند، پس به آنها نیاز داریم!!!!!!"

شاید بد نبود به ایشان میگفتیم: چقدر باهوش!!!!

اما با کمی دقت میتوان فهمید که همین حرف در واقع اصلی است که به ما میفهماند چرا اشاره گرها را به کار میگیریم! شما میتوانید با اشاره گری به یک شئ اشاره کنید و آنرا تغییر دهید. بعد به راحتی با تغییر آدرس اشاره گر مربوطه به یک شئ دیگر اشاره میکنید و آنرا تغییر میدهید و داستان ادامه دارد تا خسته شوید! هر چیزی –و به معنای واقعی هر چیزی- که در یک برنامه استفاده میشود در محلی از حافظه قرار گرفته است.

جالب است اگر به این صورت به اشاره گرها نگاه کنیم که آدرس داده های برنامه را در زمان اجرا در اختیار ما قرار میدهند، حتی آنهایی که در زمان اجرا ایجاد شده اند و شاید حتی نام مشخصی هم نداشته باشند.

 

3. استفاده از اشاره گرها در دلفی

گفته شد اشاره گرها هم یک نوع داده هستند، پس باید امکان تعریف کردن آنها را داشته باشیم، اما چگونه؟

برای تعریف اشاره گرها از علامت ^ استفاده میشود. مثال زیر تعریف اشاره گری به یک داده از نوع Integer را نمایش میدهد:

کد:

Var

 pntMyInt: ^Integer;

اما بعد از تعریف اشاره گر چگونه یک آدرس به آن نسبت دهیم؟ گفته شد اشاره گرها میتوانند به هر چیزی اشاره کنند، یک متغییر، یک تابع یا هر چیز دیگری، برای نسبت دادن هر کدام از اینها به یک اشاره گر از علامت @ استفاده میشود:

کد:

Var tmpInt: Integer; pntMyInt:^Integer; Begin pntMyInt :=@tmpInt; End;  

دقت شود کد فوق مقدار متغییر tmpInt را به pntMyInt منتقل نمیکند، بلکه فقط آدرس محل حافظه ای که tmpInt در آن قرار دارد را در pntMyInt ذخیره میکند. فرض کنید بخواهیم مقدار ذخیره شده در tmpInt را تغییر دهیم. کد زیر را ببینید:

کد:

Var tmpInt: Integer; pntMyInt:^Integer; Begin tmpInt :=100;   pntMyInt :=@tmpInt;   pntMyInt^:=102; End;  

کد فوق را بررسی میکنیم؛ در ابتدای کار با استفاده از روش مقدار دهی مستقیم عدد 100 را به متغییر tmpInt نسبت دادیم. سپس در مراحل بعدی آدرس محل حافظه این متغییر را در اشاره گر pntMyInt ذخیره کردیم، توجه توجه! این کار با مساوی قرار دادن دو متغییر کاملا متفاوت است و در اینجا عدد 100 در اشاره گر ما قرار نمیگیرد.

اما pntMyInt^ := 102 به چه معنی است؟ خیلی راحت: عدد 102 را در محل حافظه ای که pntMyInt به آن اشاره دارد ذخیره کن، و آن محل حافظه جایی نیست جز متغییر tmpInt. چه اتفاقی افتاد؟ tmpInt := 102 ، به همین راحتی! هر گاه علامت ^ بعد از اشاره گر قرار گرفت مقدار ذخیره شده در آن محل حافظه مد نظر است.

خوب! پس ما میتوانیم محل حافظه ای که اشاره گر به آن اشاره میکند را بخوانیم یا بنویسیم (تغییر دهیم)، همچنین مقدار متغیری که در محل حافظه ای که اشاره گر به آن اشاره دارد را بخوانیم یا بنویسیم.

گفته شد که اشاره گرها به اشیاء هم اشاره میکنند پس نباید عجیب باشد اگر جایی ببینیم:

کد:

formPointer : ^TForm1;

اجازه دهید کمی درگیرتر شویم! با یک کد شروع میکنیم:

کد:

type TMyObject =record    MyCode: Integer;    MyText:String; end;   pntMyObject =^TMyObject; procedure CheckMyObjectPointer; var   Object1, Object2: TMyObject;   Pointer2Object: pntMyObject; begin   Pointer2Object :=@Object1;   Pointer2Object^.MyCode :=25;   Pointer2Object^.MyText := 'First';   Pointer2Object :=@Object2;   Pointer2Object^.MyText := 'Second'; end;  

صبر کنید! هنوز برای فرار کردن خیلی زوده!

به نظر پیچیده هست ولی اگر کمی دقت کنیم خیلی هم مشکل نیست. بیایید با هم این کد را بررسی کنیم:

در قسمت تعریف داده ها یک نوع شئ شخصی تعریف شد، خیلی هم مهم نیست، شما میتوانید از اشیاء موجود دلفی استفاده کنید. اما در قسمت دوم تعریف انواع داده ها هم اتفاق خاصی نیافتاده است. این قسمت را اینجا آوردم تا با این حالت هم آشنا شوید. شما میتوانید انواع اشاره گرها را به این صورت استفاده کنید، در واقع در اینجا هر وقت در برنامه بخواهیم اشاره گری به TMyObject تعریف کنید به جای ^TMyObject به راحتی از pntMyObject استفاده میکنید. اما در رویه (Procedure) نوشته شده، ابتدا در بخش تعریف متغیرها دو شئ از نوع TMyObject تعریف میشود ونیز یک اشاره گر به همان نوع شئ. در ابتدای رویه اشاره گر با آدرس شئ اول تنظیم میشود و خیلی راحت بااستفاده از مواردی که در بالا گفته شد مقدار دهی میشود. سپس اشاره گر ما به شئ دوم نسبت داده میشود و آدرس محل حافظه شئ دوم در آن قرار میگیرد و باقی ماجرا...

خواص (Property) یک شئ را در نظر بگیرید که به این صورت قابل تغییر هستند!

شاید از خودتان بپرسید چرا من مسیر خود را طولانی کنم؟ گفته بودم که دردلفی نیاز به اشاره گرها مانند C و C++ احساس نمیشود، اما به شما میگویم گاهی این کار مسیر شما را خیلی بیشتر از آن چیزی که فکر میکنید کوتاه میکند، استفاده از بعضی توابع API را در نظر بگیرید...

4. اشاره گرهای عمومی در دلفی

تا اینجا از اشاره گرهایی استفاده کردیم که به نوع داده مشخصی اشاره داشتند، در این حالت دلفی به اندازه کافی باهوش هست که اشتباهات ما را در استفاده از اشاره گر به ما گوشزد کند. اما گاهی مجبوریم از اشاره گرهای بی نام و نشانی استفاده کنیم که به آنها اشاره گر عمومی میگوییم و قادرند به هر چیزی بدون در نظر گرفتن نوع آن اشاره کنند. این نوع اشاره گرها در دلفی به نوع داده ای Pointer اطلاق میشوند. به مثال زیر توجه کنید:

کد:

procedure CheckGenericPointer(const pntGPointer: Pointer); var tmpInt:^Integer; MyCode: Integer; begin tmpInt := pntGPointer; MyCode := tmpInt^; end;   procedure CallCheckProc; var MyInt: Integer; begin MyInt :=100; CheckGenericPointer(

/ 0 نظر / 27 بازدید