عالی
خوب
متوسط
بد
خیلی بد

یک مثال: مدیریت پیغام
این مثال از کلاسی است که نیاز به کنترل کپیها دارد تا فضا را ساماندهی کند. ما دو کلاس را ترسیم میکنیم که میتوانند در یک برنامه مدیریت پستی استفاده شوند. این کلاسها یعنی Message و Folder به ترتیب نماینده پیغام، و پوشهای که یک پیغام میتواند در آن قرار گیرد هستند. یک پیغام میتواند در بیش از یک پوشه ظاهر شود. ما عملیات save و remove را روی Message داریم که پیغام را در یک پوشه مشخص ذخیره کرده یا از آن حذف میکند.
به جای اینکه یک کپی از هر پیغام را در هر پوشهای ذخیره کنیم، هر Message گروهی از اشارهگرها را به پوشههایی که این پیغام در آنها ظاهر میشود نگهداری میکند. هر پوشه هم اشارهگرهایی به پیغامهایی که در آن است خواهد داشت.
تصویر زیر، ساختار دادهای که پیادهسازی خواهیم کرد را نشان میدهد:
طرح کلاس Message و Folder
وقتی Message جدیدی میسازیم، محتویات آن را مشخص میکنیم، اما زمانی که بخواهیم آن را درون یک پوشه قرار دهیم، save را فراخوانی میکنیم.
وقتی پیغامی را کپی میکنیم، هم محتویات آن را کپی میکنیم و هم مجموعه اشارهگرهای Folder. همچنین باید اشارهگری به این پیغام را در هر پوشهای که به پیغام کپی شده اشاره میکند اضافه کنیم.
انتساب یک پیغام به دیگری، مشابه کپی کردن پیغام است: پس از انتساب، محتویات و مجموعه پوشهها یکسان خواهند بود. کار را با حذف پیغام سمت چپ عملگر انتساب از پوشههایی که در آنست آغاز میکنیم. وقتی که پیغام قدیمی حذف شد، محتویات و مجموعه پوشهها را از عملوند سمت راست در سمت چپی کپی میکنیم. همچنین باید اشارهگری به پیغام سمت چپ را به هر پوشهای از گروه اضافه کنیم.
وقتی پیغامی را نابود میکنیم، باید هر پوشهای که به پیغام اشاره میکند را هم بروز رسانی کنیم. وقتی پیغام نابود شود، آن اشارهگرها فایدهای ندارند، پس باید هر اشارهگری به این پیغام را از هر پوشهای در گروه پوشههای این پیغام حذف کنیم.
با نگاه به لیست عملیات، میتوان دید که مخرب و عملگر انتساب، کار حذف پیغام از پوشههایی که به آن اشاره میکنند را بطور مشابه انجام میدهند. همچنین سازنده کپی و عملگر انتساب، کار اضافه کردن یک پیغام به گروهی از پوشهها را بطور مشابه انجام میدهند. یک جفت تابع سودمند خصوصی برای انجام این اعمال تعریف خواهیم کرد.
کلاس Message
با طرحی که داریم، میتوانیم مقدار قابل قبولی از کلاس Message را بنویسیم:
کد:class Message { public: // folders is initialized to the empty set automatically Message(const string &str = ""): contents (str) { } // copy control: we must manage pointers to this Message // from the Folders pointed to by folders Message(const Message&); Message& operator=(const Message&); ~Message(); // add/remove this Message from specified Folder's set of messages void save (Folder&); void remove(Folder&); private: string contents; // actual message text std::set<Folder*> folders; // Folders that have this Message // Utility functions used by copy constructor, assignment, and destructor: // // Add this Message to the Folders that point to the parameter void put_Msg_in_Folders(const std::set<Folder*>&); // remove this Message from every Folder in folders void remove_Msg_from_Folders(); };
این کلاس دو عضو دادهای تعریف میکند: contents که یک string برای نگهداری متن پیغام است، و folders که مجموعهای از اشارهگرهای به Folder است که این پیغام در آنها ظاهر میشود.
سازنده یک پارامتر تنها از نوع string میگیرد که محتویات پیغام را دارد. سازنده یک کپی از متن پیغام را در contents ذخیره میکند و بطور ضمنی مجموعه پوشهها را با مجموعه تهی مقداردهی اولیه میکند. این سازنده یک آرگومان پیشفرض فراهم میکند که رشته تهی هست. پس به عنوان سازنده پیشفرض Message هم سرویس میدهد.
توابع سودمند اعمالی را فراهم میکنند که بین اعضای کنترل کپی مشترک هستند. تابع put_Msg_in_Folders یک اشارهگر از Message خودش را به پوشههایی که باید به آن پیغام اشاره کنند اضافه میکند. این تابع توسط سازنده کپی و عملگر انتساب استفاده میشود.
تابع remove_Msg_from_Folders توسط عملگر انتساب و مخرب استفاده میشود و اشارهگر به پیغام را از هر پوشهای که در عضو folders وجود دارد حذف میکند.
کنترل کپی برای کلاس Message
وقتی یک پیغام را کپی میکنیم، باید پیغام جدیدی که ساخته میشود را به هر پوشهای که پیغام در حال کپی شدن در آن ظاهر میشود، اضافه کنیم. این بیش از کاری است که سازنده ساخته شده توسط کامپایلر برای ما انجام میدهد. پس ما باید سازنده کپی خودمان را تعریف کنیم:
سازنده کپی، اعضای دادهای شیء جدید را با یک کپی از اعضای پارامترش مقدار اولیه میدهد. علاوه بر این مقداردهیهای اولیه که سازنده کپی ساخته شده توسط کامپایلر هم میتوانست برای ما انجام دهد، باید در عناصر folders حرکت کنیم و پیغام را به هرکدام از پوشههای درون این مجموعه اضافه کنیم. سازنده کپی از تابع put_Msg_in_Folders برای این کار استفاد میکند.کد:Message::Message(const Message &m): contents(m.contents), folders(m.folders) { // add this Message to each Folder that points to m put_Msg_in_Folders(folders); }
توجه
وقتی سازنده کپی خودمان را مینویسیم باید هر عضوی که میخواهیم کپی شود را بطور صریح کپی کنیم. یک سازنده کپی که بطور صریح تعریف شده است، هیچ چیز را بطور خودکار کپی نمیکند.
مانند هر سازنده دیگری، اگر یک عضو کلاس را مقداردهی اولیه نکنیم آن عضو با استفاده از سازنده پیشفرض خودش مقداردهی اولیه میشود. مقداردهی اولیه پیشفرض در یک سازنده کپی، از سازنده کپی عضو استفاده نمیکند.
ویرایش توسط Mohammad Rastkar : Mar-31-2010 در ساعت 06:37 PM
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید... (جالب بود، لینک بدید)
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید... : آتاری - نینتندو - PC - سگا
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...

عضو put_Msg_in_Folders
این تابع در بین اشارهگرهای درون عضو folders از پارامتر rhs پیمایش میکند. این اشارهگرها پوشههایی را مشخص میکنند که به rhs اشاره میکنند. ما باید یک اشارهگر به این پیغام را به هرکدام از این پوشهها اضافه کنیم.
این تابع با حلقهای روی rhs.folders و فراخوانی عضو addMsg از Folder این کار را انجام میدهد:
تنها قسمت نکتهدار در این تابع، فراخوانی addMsg است: با (beg*) شروع میشود که به تکرارگر ارجاع میدهد. این اشارهگری به یک پوشه را نتیجه میدهد. سپس عملگر پیکان را روی اشارهگر پوشه اعمال میکند تا عملکرد addMsg را اجرا کند. ما this را پاس میدهیم، یعنی اشارهگری به پیغامی که میخواهیم به پوشه اضافه کنیم.کد:// add this Message to Folders that point to rhs void Message::put_Msg_in_Folders(const set<Folder*> &rhs) { for(std::set<Folder*>::const_iterator beg = rhs.begin(); beg != rhs.end(); ++beg) (*beg)->addMsg(this); // *beg points to a Folder }
عملگر انتساب Message
انتساب پیچیدهتر از سازنده کپی است. مانند سازنده کپی، انتساب باید contents را انتساب دهد و folders را بروز رسانی کند تا با عملوند سمت راست مطابقت کند. همچنین باید این پیغام را به هرکدام از پوشههایی که به rhs اشاره میکنند اضافه کند. میتوانیم از تابع put_Msg_in_Folders استفاده کنیم تا این بخش از انتساب را انجام دهیم.
قبل از کپی کردن از rhs، ابتدا باید این پیغام را از هر پوشهای که فعلاً به آن اشاره میکند حذف کنیم (با حرکت در folders). تابعی به نام remove_Msg_from_Folders این کار را انجام میدهد.
با داشتن دو تابع put_Msg_in_Folders و remove_Msg_from_Folders، عملگر انتساب ساده میشود:
عملگر انتساب با چک کردن اینکه عملوندهای سمت چپ و راست یکی نباشند شروع میکند. این چک کردن را به دلایلی انجام میدهیم که با دیدن بقیه تابع مشخص میشود. با فرض اینکه عملوندها اشیاء متفاوتی هستند، تابع remove_Msg_from_Folders را صدا میزنیم تا این پیغام را از هر پوشهای که در عضو folders هست حذف کنیم. سپس باید اعضای contents و folders را از عملوند سمت راست به این شیء انتسابت دهیم. در آخر، put_Msg_in_Folders را صدا میزنیم تا اشارهگری به این پیغام را در هر پوشهای که به rhs هم اشاره میکند اضافه کنیم.کد:Message& Message::operator=(const Message &rhs) { if (&rhs != this) { remove_Msg_from_Folders(); // update existing Folders contents = rhs.contents; // copy contents from rhs folders = rhs.folders; // copy Folder pointers from rhs // add this Message to each Folder in rhs put_Msg_in_Folders(rhs.folders); } return *this; }
حالا که کار remove_Msg_from_Folders را دیدیم، متوجه میشویم چرا عمل انتساب را با چک کردن اینکه اشیاء متفاوت هستند شروع کردیم. انتساب با حذف عملوند سمت چپ همراه است. وقتیکه اعضای عملوند سمت چپ از بین بروند، آنهایی که در عملوند سمت راست است به اعضای متناظر سمت چپ انتساب داده میشوند. اگر اشیاء یکسان باشند، پس از بین بردن اعضای سمت چپ همچنین اعضای سمت راست را هم نابود میکند!
توجه
خیلی مهم است که عملگر انتساب حتی وقتی که شیء به خودش انتساب داده میشود درست کار کند. یک راه معمول برای اطمینان از این عملکرد، چک کردن صریح برای انتساب به خود است.
عضو remove_Msg_from_Folders
پیادهسازی این تابع همانند put_Msg_in_Folders است، مگر اینکه اینبار remMsg را صدا میزنیم تا این پیغام را از هر پوشهای که در folders بدان اشاره میشود حذف کنیم:
کد:// remove this Message from corresponding Folders void Message::remove_Msg_from_Folders() { // remove this message from corresponding folders for(std::set<Folder*>::const_iterator beg = folders.begin (); beg != folders.end (); ++beg) (*beg)->remMsg(this); // *beg points to a Folder }
مخرب Message
تابع کنترل کپی باقیماندهای که باید پیادهسازی کنیم، مخرب است:
با استفاده ار تابع remove_Msg_from_Folders، نوشتن مخرب کاری ندارد. این تابع را برای پاکسازی folders فراخوانی میکنیم. سیستم بطور خودکار مخرب string را برای آزادسازی contents و مخرب set را برای رهاسازی حافظه گرفته شده توسط عضو folders فراخوانی میکند. بنابراین، تنها کاری که برای مخرب Message میماند، صدا زدن remove_Msg_from_Folders است.کد:Message::~Message() { remove_Msg_from_Folders(); }
توصیه
عملگر انتساب اغلب همان کاری را انجام میدهد که در سازندهکپی و مخرب نیاز است. در این موارد، کار مشترک باید در توابع سودمند خصوصی قرار گیرد.
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید... (جالب بود، لینک بدید)
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید... : آتاری - نینتندو - PC - سگا
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...
آقای مهندس راستکار بی صبرانه منتظر ادامه مطالب شما هستیم .
با تشکر
ویرایش توسط tajerman : May-10-2010 در ساعت 12:10 PM

مدیریت اعضای اشارهگر
استفاده از کتابخانه استاندارد توصیه میشود؛ یکی از دلایل اینست که استفاده از کتابخانه استاندارد نیاز به اشارهگرها در برنامههای مدرن ++C را کاهش میدهد. اما هنوز هم بسیاری از برنامهها نیاز به استفاده از اشارهگرها دارند، بطور خاص در پیادهسازی کلاسها. کلاسهای دارای اشارهگر نیاز به توجه دقیق بر کنترلکپی دارند. دلیل اینست که کپی کرن یک اشارهگر فقط آدرس درونش را کپی میکند نه شیءای که بدان اشاره میکند.
هنگام طراحی یک کلاس دارای عضو اشارهگر، اولین موردی که سازنده کلاس باید تصمیم بگیرد، نحوه عملکرد اشارهگر است. وقتی یک اشارهگر را در دیگری کپی میکنیم، هر دوی آنها به یک شیء اشاره خواهند کرد. وقتی دو اشارهگر به یک شیء اشاره میکنند، میتوان از هرکدام از آنها برای دسترسی و تغییر شیء اشاره شده استفاده کرد. همچنین، توسط یکی از اشارهگرها میتوان شیء را نابود کرد در عینحالیکه استفاده کننده از اشارهگر دیگر فکر میکند که هنوز شیء وجود دارد.
بطور پیشفرض، یک عضو اشارهگر همان رفتار هر شیء اشارهگر دیگری را دارد. اما تغییر شیوه کنترلکپی بر شیوه رفتار اشارهگرها تأثیر دارد. کلاسهای ++C بیشتر از سه روش برای مدیریت اشارهگرها استفاده میکنند:
در این قسمت به کلاسهایی که این رفتارها را برای اعضای اشارهگر پیادهسازی میکنند میپردازیم.
- اشارهگر عضو همان عملکرد معمول اشارهگرها را دارد. این چنین کلاسهایی با تمام خطرات اشارهگرها روبرو میشوند اما به کنترلکپی خاصی نیاز ندارند.
- این کلاسها اشارهگرهایی که به اصطلاح عملکرد 'هوشمند' دارند را پیادهسازی میکنند. شیءای که اشارهگرها بدان اشاره میکنند مشترک است، اما از اشارهگرهای معلق جلوگیری میشود.
- کلاس میتواند رقتار مقدارگرا را ارائه دهد. شیءای که اشارهگر بدان اشاره میکند منحصربهفرد خواهد بود و توسط هر شیء از کلاس بطور جداگانه مدیریت میشود.
یک کلاس ساده با یک عضو اشارهگر
برای نشان دادن موضوعاتی که مطرح است، یک کلاس ساده را پیادهسازی میکنیم که یک int و یک اشارهگر دارد:
کد:// class that has a pointer member that behaves like a plain pointer class HasPtr { public: // copy of the values we're given HasPtr(int *p, int i): ptr(p), val(i) { } // const members to return the value of the indicated data member int *get_ptr() const { return ptr; } int get_int() const { return val; } // non const members to change the indicated data member void set_ptr(int *p) { ptr = p; } void set_int(int i) { val = i; } // return or change the value pointed to, so ok for const objects int get_ptr_val() const { return *ptr; } void set_ptr_val(int val) const { *ptr = val; } private: int *ptr; int val; };
سازنده HasPtr دو پارامتر میگیرد که به اعضای دادهای کلاس کپی میکند. این کلاس توابع دسترسی سادهای تعریف میکند: توابع ثابت get_int و get_ptr که مقادیر اعضا را برمیگردانند. توابع set هم مقادیر را انتساب میدهند، که عضو اشارهگر به مکان دیگری اشاره خواهد کرد. اعضای get_ptr_val و set_ptr_val با مقداری که اشارهگر بدان اشاره میکند سر و کار دارد.
انتساب/کپی پیشفرض
چون کلاس یک سازنده کپی تعریف نکرده است، کپی کردن اشیاء با کپی کردن اعضایشان انجام میشود:
کد:int obj = 0; HasPtr ptr1(&obj, 42); // int* member points to obj, val is 42 HasPtr ptr2(ptr1); // int* member points to obj, val is 42
پس از کپی، اشارهگرهای درون ptr1 و ptr2 هر دو، آدرس یک شیء را دارند و مقدار int هم یکسان خواهد بود. اما رفتار این دو عضو اشارهگر کاملاً متفاوت است، چون مقدار یک اشارهگر از مقدار شیءای که بدان اشاره میکند جداست. پس از کپی، مقادیر int ها از هم جدا و نامربوط اند، درحالیکه اشارهگرها به هم ارتباطی دارند (چون به یک شیء واحد اشاره میکنند).
توجه
کلاسهایی که اعضای اشارهگر دارند و از کنترلکپی پیشفرض تولید شده توسط کامپایلر استفاده میکنند، در معرض تمام خطرات معمول اشارهگرها هستند. به ویژه اینکه کلاس خودش راهی برای جلوگیری از اشارهگرهای معلق ندارد.
اشارهگرها یک شیء واحد را به اشتراک میگذارند
وقتی یک مقدار محاسبهای را کپی میکنیم، مقدار کپی به اصل مربوط نیست. میتوانیم یکی از کپیها را جدا از دیگری تغییر دهیم:
وقتی یک اشاره را کپی میکنیم مقادیر آدرسها جدا از هم نگهداری میشوند، اما اشارهگرها به یک شیء واحد اشاره خواهند کرد. تابع set_ptr_val را برای هرکدام از اشیاء که صدا بزنیم شیء مورد اشاره، برای هر دو تغییر خواهد کرد (چون فقط یک شیء واحد است):کد:ptr1.set_int(0); // changes val member only in ptr1 ptr2.get_int(); // returns 42 ptr1.get_int(); // returns 0
وقتی دو اشارهگر به یک شیء واحد اشاره میکنند، هرکدام از آنها میتواند مقدار شیء مشترک را تغییر دهد.کد:ptr1.set_ptr_val(42); // sets object to which both ptr1 and ptr2 point ptr2.get_ptr_val(); // returns 42
امکان داشتن اشارهگرهای معلق
چون کلاس ما اشارهگرها را بطور مستقیم کپی میکند، ممکن است کاربران را با یک مشکل روبرو کند: HasPtr اشارهگری که بدان داده شده است را ذخیره میکند. تضمین اینکه تا زمان ماندگاری شیء HasPtr، شیء مورد اشاره هم وجود داشته باشد به عهده خود کاربر است:
کد:int *ip = new int(42); // dynamically allocated int initialized to 42 HasPtr ptr(ip, 10); // HasPtr points to same object as ip does delete ip; // object pointed to by ip is freed ptr.set_ptr_val(0); // disaster: The object to which HasPtr points was freed!
مسئلهای که در اینجا هست اینست که ip و اشارهگر درون ptr هر دو به یک شیء اشاره میکنند. وقتی شیء حذف شود اشارهگر HasPtr دیگر به یک شیء معتبر اشاره نمیکند (معلق میشود). اما متوجه نمیشویم که شیء حذف شده است.
ویرایش توسط Mohammad Rastkar : Jun-20-2010 در ساعت 02:50 PM
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید... (جالب بود، لینک بدید)
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید... : آتاری - نینتندو - PC - سگا
- برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...

تعریف کلاسهای اشارهگر هوشمند
در قسمت قبل، کلاس سادهای را تعریف کردیم که یک اشارهگر و یک int را نگهداری میکرد. عضو اشارهگر رفتار معمول را داشت. هر تغییری در شیءای که اشارهگر بدان اشاره میکرد، به یک شیء تک و مشترک اعمال میشود. اگر کاربر آن شیء را حذف میکرد، اشارهگر کلاس پادرهوا میشد، چون به محل شیءای اشاره میکرد که دیگر وجود نداشت.
یک جایگزین برای داشتن یک عضو اشارهگر بطوری که دقیقاً مانند یک اشارهگر رفتار کند، تعریف کلاسی است که گاهی اشارهگر هوشمند گفته میشود. یک اشارهگر هوشمند مانند اشارهگر معمولی رفتار میکند، اما عملکردهایی را هم اضافه میکند. در اینجا ما به اشارهگر هوشمند مسئولیت حذف شیء مشترک را میدهیم. کاربران بطور پویا یک شیء را میسازند و آدرس آن شیء را به کلاس HasPtr جدید ما ارسال میکنند. کاربر ممکن است هنوز توسط یک اشارهگر ساده به شیء دسترسی داشته باشد، اما نباید اشارهگر را حذف(delete) کند. کلاس HastPtr اطمینان حاصل میکند که شیء وقتی که آخرین HasPtr اشاره کننده به آن از بین رفت، حذف شود.
در موارد دیگر، HastPtr ما مانند یک اشارهگر ساده رفتار میکند. وقتی یک شیء HasPtr را کپی میکنیم، شیء کپی شده و اصلی به یک شیء اشاره خواهند کرد. اگر آن شیء را از طریق یکی از کپیها تغییر دهیم، از طریق کپی دیگر هم به همان شیء تغییر یافته دسترسی خواهیم داشت.
کلاس HasPtr جدید ما به یک مخرب نیاز دارد تا اشارهگر را حذف کند. اما مخرب نباید اشارهگر را بدون هیچ شرطی حذف کند. اگر دو HasPtr به یک شیء مشترک اشاره کنند، نباید شیء را حذف کنیم تا وقتیکه هر دو شیء HasPtr نابود شوند. برای نوشتن مخرب باید بدانیم که این HasPtr آخرین شیءای است که به یک شیء مشترک اشاره میکند.
ادامه مطالب در برای مشاهده لینک ها ، باید در انجمن ثبت نام کنید. برای ثبت نام اینجا کلیک کنید...
در حال حاضر 1 کاربر در حال مشاهده این موضوع است. (0 کاربران و 1 مهمان ها)
علاقه مندی ها (Bookmarks)