0

آموزش ++C - بخش هفتم

 
reza1371
reza1371
کاربر برنزی
تاریخ عضویت : مهر 1388 
تعداد پست ها : 168
محل سکونت : لرستان

آموزش ++C - بخش هفتم
چهارشنبه 22 اردیبهشت 1389  10:01 AM

كلاس GradeBook با يك عضو داده، يك تابع set و get

در اين مثال، كلاس GradeBook (برنامه شكل 5-3) مبادرت به نگهداري نام دوره بصورت يك عضو داده مي‌كند و از اينروست كه مي‌تواند بكار گرفته شده يا در هر زمان اجراي برنامه آن را اصلاح كرد. كلاس حاوي توابع عضو setCourseName ، getCourseName و displayMessage است. تابع عضو setCourseName نام دوره را در عضو داده GradeBook ذخيره مي‌سازد. تابع عضو getCourseName نام دوره را از عضو داده بدست مي‌آورد. تابع عضو displayMessage كه هيچ پارامتري ندارد، پيغام خوش‌آمدگويي را كه شامل نام دوره است، به نمايش در مي‌آورد. با اين همه، همانطوري كه مشاهده مي‌كنيد، در حال حاضر تابع، نام دوره را با فراخواني تابع ديگري در همان كلاس بدست مي‌آورد، تابع getCourseName .

1   // Fig. 3.5: fig03_05.cpp

2   // Define class GradeBook that contains a courseName data member

3   // and member functions to set and get its value;

4   // Create and manipulate a GradeBook object.

5   #include <iostream>

6   using std::cout;

7   using std::cin;

8   using std::endl;

9    

10 #include <string> // program uses C++ standard string class

11 using std::string;

12 using std::getline;

13  

14 // GradeBook class definition

15 class GradeBook

16 {

17 public:

18    // function that sets the course name

19    void setCourseName( string name )

20    {     

21       courseName = name; // store the course name in the object

22    } // end function setCourseName

23    

24    // function that gets the course name

25    string getCourseName()

26    {

27       return courseName; // return the object's courseName

28    } // end function getCourseName

29  

30    // function that displays a welcome message

31    void displayMessage()

32    {

33       // this statement calls getCourseName to get the

34       // name of the course this GradeBook represents

35      cout << "Welcome to the grade book for\n" << getCourseName() << "!"

36          << endl;

37    } // end function displayMessage

38 private:

39    string courseName; // course name for this GradeBook

40 }; // end class GradeBook 

41  

42 // function main begins program execution

43 int main()

44 {

45    string nameOfCourse; // string of characters to store the course name

46    GradeBook myGradeBook; // create a GradeBook object named myGradeBook

47    

48    // display initial value of courseName

49    cout << "Initial course name is: " << myGradeBook.getCourseName()

50       << endl;

51  

52    // prompt for, input and set course name

53    cout << "\nPlease enter the course name:" << endl;

54    getline( cin, nameOfCourse ); // read a course name with blanks

55    myGradeBook.setCourseName( nameOfCourse ); // set the course name

56  

57    cout << endl; // outputs a blank line

58    myGradeBook.displayMessage(); // display message with new course name

59    return 0; // indicate successful termination

60 } // end main

Initial course name is:

Please enter the course name:

CS101 Introduction to C++ Programming

Welcome to the grade book for

CS101 Introduction to C++ Programming!

شكل 5-3 | تعريف و تست كلاس GradeBook با يك عضو داده و توابع set و get .

 

 

تصريح‌كننده دسترسي public و private

اكثر اعلان‌هاي اعضاي داده پس از برچسب تصريح‌كننده دسترسي private: ظاهر مي‌شوند (خط 38 ). همانند public ، كلمه كليدي private يك تصريح‌كننده است. متغيرها يا توابع اعلان شده پس از private (و قبل از تصريح‌كننده دسترسي بعدي) فقط در توابع عضو كلاسي كه در آن اعلان شده‌اند،  در دسترس خواهند بود. بنابر اين، عضو داده CourseName فقط مي‌تواند در توابع عضو setCourseName ، getCourseName و displayMessage از كلاس GradeBook بكار گرفته شود. بدليل اينكه عضو داده   CourseName بصورت private اعلان شده است، نمي‌تواند توسط توابع خارج از كلاس (همانند main ) يا توابع عضو كلاس‌هاي ديگر در برنامه بكار گرفته شود. مبادرت به دسترسي عضو داده courseName در يكي از نقاط اين برنامه با عبارتي همانند myGradeBook.courseName خطاي كامپايل توليد كرده و پيغامي مشابه

 

c annot access private member declared in class 'GradeBook'

به نمايش در مي‌آيد.

 

مهندسي نرم‌افزار

بعنوان يك قانون، اعضاي داده بايستي بصورت private و توابع عضو بصورت public اعلان شوند.  (مشاهده خواهيد كرد در صورتيكه توابع عضو خاصي كه فقط از طريق توابع عضو ديگر موجود در كلاس در دسترس قرار مي‌گيرند، اگر بصورت private اعلان شوند، مناسب خواهند بود.)

 

خطاي برنامه‌نويسي

اقدام يك تابع، كه عضوي از يك كلاس خاص نيست، به دسترسي به يك عضو private آن كلاس، خطاي كامپايل بدنبال خواهد داشت.

دسترسي پيش‌فرض براي اعضاي كلاس حالت private است، از اينرو تمام اعضاي قرار گرفته پس از سرآيند و قبل از اولين تصريح‌كننده دسترسي بصورت private خواهند بود. امكان دارد تصريح‌كننده‌هاي دسترسي public و private تكرار شوند، اما اينكار ضرورتي ندارد و مي‌تواند سبب سردرگمي شود.

 

برنامه‌نويسي ايده‌ال

عليرغم اين واقعيت كه تصريح‌كننده‌هاي دسترسي public و private مي‌توانند تكرار شده و جابجا نوشته شوند، اما سعي كنيد تمام ليست عضوهاي public يك كلاس را در يك گروه و تمام عضوهاي private را در گروه ديگري قرار دهيد. در اين حالت توجه سرويس‌گيرنده بر روي واسط public كلاس متمركز مي‌شود، بجاي اينكه توجه آن به پياده‌سازي كلاس معطوف شود.

 

برنامه‌نويسي ايده‌ال

اگر مي‌خواهيد ليست اعضاي private را در ابتداي تعريف كلاس قرار دهيد، بصورت صريح از كلمه private استفاده كنيد عليرغم اينكه private انتخاب پيش‌فرض است. در اينحالت وضوح برنامه افزايش مي‌يابد.

اعلان اعضاي داده با تصريح‌كننده دسترسي private ، بعنوان پنهان‌سازي داده شناخته مي‌شود. زمانيكه برنامه مبادرت به ايجاد (نمونه‌سازي) از يك شي كلاس GradeBook مي‌كند، عضو داده courseName در شي كپسوله (پنهان) شده و فقط مي‌تواند توسط توابع عضو آن كلاس در دسترس قرار گيرد. در كلاس GradeBook ، توابع عضو setCourseName و getCourseName مبادرت به دستكاري مستقيم عضو داده courseName مي‌كنند (اگر لازم باشد diaplayMessage هم مي‌تواند اين كار را انجام دهد).

 

مهندسي نرم‌افزار

در فصل دهم خواهيد آموخت كه توابع و كلاس‌هاي اعلان شده توسط يك كلاس حالت دوست (friend) پيدا كرده و مي‌توانند به اعضاي private كلاس دسترسي پيدا كنند.

 

اجتناب از خطا

با ايجاد اعضاي داده كلاس بصورت private و توابع عضو كلاس بصورت public ، كار خطايابي آسانتر مي‌شود چرا كه دستكاري‌كننده‌هاي داده محلي هستند.

توابع عضو setCourseName و getCourseName

تابع عضو setCourseName تعريف شده در خطوط 19-22 به هنگام اتمام وظيفه خود هيچ مقداري برگشت نمي‌دهد و از اينرو نوع برگشتي آن void است. تابع عضو يك پارامتر دريافت مي‌كند، name ، كه نشان‌دهنده نام دوره‌اي است كه به آن بعنوان يك آرگومان ارسال خواهد شد (خط 55 از main ).

خط 21 مبادرت به تخصيص name به عضو داده courseName مي‌كند. در اين مثال، setCourseName مبادرت به اعتبارسنجي نام دوره نمي‌كند (بررسي فرمت نام دوره با يك الگوي خاص). براي نمونه فرض كنيد كه دانشگاهي مي‌تواند از اسامي دوره تا 25 كاراكتر يا كمتر چاپ بگيرد. در اين مورد، مايل هستيم كلاس GradeBook ما را مطمئن سازد كه عضو داده courseName هرگز بيش از 25 كاراكتر نداشته باشد. در بخش 10-3 با تكنيك‌هاي اوليه اعتبارسنجي آشنا خواهيد شد.

تابع عضو getCourseName تعريف شده در خطوط 25-28 يك شي خاص از GradeBook بنام courseName را برگشت مي‌دهد. تابع عضو داراي يك ليست پارامتري خالي است و از اينرو براي انجام وظيفه خود نيازي به اطلاعات اضافي ندارد. تابع مشخص مي‌سازد كه يك رشته برگشت خواهد داد. زمانيكه تابعي كه نوع برگشتي آن بجز void است فراخواني مي‌شود و وظيفه خود را انجام مي‌دهد، تابع نتيجه‌اي را به فراخوان خود برگشت مي‌دهد. براي مثال، هنگامي كه به سراغ يك ATM مي‌رويد و تقاضاي نمايش ميزان موجودي خود را مي‌كنيد، انتظار داريد ATM مقداري كه نشاندهنده ميزان موجودي است به شما برگشت دهد. به همين ترتيب، هنگامي كه عبارتي تابع عضو getCourseName از يك شي GradeBook را فراخواني مي‌كند، عبارت انتظار دريافت نام دوره را دارد (در اين مورد، يك رشته است كه توسط نوع برگشتي تابع مشخص گرديده است). اگر تابعي بنام square داشته باشيد كه مربع آرگومان خود را برگشت مي‌دهد، عبارت

int results = square(2);

مقدار 4 را از تابع square برگشت داده و متغير result را با 4 مقداردهي اوليه مي‌كند. اگر تابعي بنام maximum داشته باشيد كه بزرگترين عدد را از بين سه آرگومان خود برگشت مي‌دهد، عبارت

int biggest = maximum(27,114,41);

عدد 114 را از تابع maximum برگشت داده و متغير biggest با عدد 114 مقداردهي اوليه مي‌شود.

 

خطاي برنامه‌نويسي

فراموش كردن مقدار برگشتي از تابعي كه مقدار برگشتي براي آن در نظر گرفته شده است، خطاي كامپايل بدنبال خواهد داشت.

توجه كنيد كه عبارات قرار گرفته در خطوط 21 و 27 هر يك از متغير courseName (خط 39 ) استفاده كرده‌اند، بدون اينكه اين متغير در توابع عضو اعلان شده باشد مي‌توانيم از courseName در توابع عضو كلاس   GradeBook استفاده كنيم چرا كه courseName يك عضو داده از كلاس است. همچنين توجه نمائيد كه ترتيب تعريف توابع عضو تعيين‌كننده ترتيب فراخواني در زمان اجرا نيستند از اينرو تابع عضو getCourseName مي‌تواند قبل از تابع عضو setCourseName تعريف شود.

تابع عضو d isplayMessage

تابع عضو displayMessage در خطوط 31-37 هيچ نوع داده‌اي را پس از كامل كردن وظيفه خود برگشت نمي‌دهد، از اينروست كه نوع برگشتي آن void تعريف شده است. تابع، پارامتري دريافت نمي‌كند، بنابر اين ليست پارامتري آن خالي است. خطوط 35-36 پيغام خوش‌آمدگويي را كه حاوي مقدار عضو داده courseName است در خروجي چاپ مي‌كنند. خط 35 مبادرت به فراخواني تابع getCourseName براي بدست آوردن مقداري از courseName مي‌كند. توجه نمائيد كه تابع عضو displayMessage هم قادر به دسترسي مستقيم به عضو داده courseName است. در مورد اينكه چرا از فراخواني تابع عضو getCourseName براي بدست آوردن مقدار courseName استفاد كرده‌ايم، توضيح خواهيم داد.

تست كلاس GradeBook

تابع main (خطوط 43-60 ) يك شي از كلاس GradeBook ايجاد كرده و از توابع عضو آن استفاده مي‌كند. در خط 46 يك شي GradeBook بنام myGradeBook ايجاد شده است. خطوط 49-50 نام دوره اوليه را با فراخواني تابع عضو getCourseName به نمايش در مي‌آورند. توجه كنيد كه اولين خط خروجي نشاندهنده نام دوره نمي‌باشد، چرا كه عضو داده courseName در ابتداي كار تهي است. بطور پيش‌فرض، مقدار اوليه يك رشته، رشته تهي است، رشته‌اي كه حاوي هيچ كاراكتري نمي‌باشد. زمانيكه يك رشته تهي به نمايش درآيد، چيزي بر روي صفحه نمايش ظاهر نميشود.

خط 53 از كاربر مي‌خواهد كه نام دوره را وارد سازد. متغير محلي nameOfCourse اعلان شده در خط 45 ، با نام دوره وارد شده توسط كاربر مقداردهي مي‌شود كه از فراخواني تابع getline بدست مي‌آيد (خط 54 ). خط 55 تابع عضو setCourseName را فراخواني كرده و nameOfCourse را بعنوان آرگومان تابع بكار مي‌گيرد. به هنگام فراخواني تابع، مقدار آرگومان به پارامتر name (خط 19 ) از تابع عضو setCourseName كپي مي‌گردد (خطوط 19-22 ). سپس مقدار پارامتر به عضو داده courseName تخصيص مي‌يابد (خط 21 ). خط 57 يك خط خالي در خروجي قرار داده، سپس خط 58 تابع عضو displayMessage را براي نمايش پيغام خوش‌آمدگويي به همراه نام دوره فراخواني مي‌كند.

مهندسي نرم‌افزار با توابع set و get

اعضاي داده private يك كلاس فقط قادر به دستكاري شدن از طريق توابع عضو آن كلاس هستند (و «دوستان» آن كلاس كه در فصل دهم با آنها آشنا خواهيد شد). بنابر اين سرويس‌گيرنده يك شي، (يعني هر كلاس يا تابعي كه مبادرت به فراخواني توابع عضو از خارج شي مي‌كند)، توابع عضو public كلاس را براي دريافت سرويس‌‌هاي كلاس براي شي‌هاي خاصي از كلاس فراخواني مي‌كند. به همين دليل است كه عبارات موجود در تابع main (برنامه شكل 5-3، خطوط 43-60 ) مبادرت به فراخواني توابع عضو setCourseName ، getCourseName و displayMessage موجود در شي GradeBook مي‌كند. غالباً توابع عضو public كلاس‌ها به سرويس‌گيرنده‌ها اجازه تخصيص مقادير به (set) يا دريافت مقادير از (get) اعضاي داده private را مي‌دهند. نيازي نيست كه اسامي اين توابع عضو حتماً با set يا get شروع شوند، اما اين روش نام‌گذاري مرسوم است. در اين مثال، تابع عضوي كه مبادرت به تنظيم (تخصيص مقادير) عضو داده courseName مي‌كند، setCourseName نام دارد و تابع عضوي كه مقداري از عضو داده courseName بدست مي‌آورد، تابع getCourseName ناميده مي‌شود. توجه كنيد كه گاهاً توابع set ، بنام mutators شناخته مي‌شوند (چرا كه مبادرت به تغيير يا اصلاح مقادير مي‌كنند)، و توابع get بنام accessors ناميده مي‌شوند (چرا كه به مقادير دسترسي پيدا مي‌كنند).

بخاطر داريد كه اعلان اعضاي داده با تصريح‌كننده دسترسي private موجب پنهان‌سازي داده مي‌شود. تدارك ديدن توابع set و get به سرويس‌گيرنده‌هاي كلاس اجازه دسترسي به داده‌هاي پنهان شده را مي‌دهند، اما بصورت غيرمستقيم. سرويس‌گيرنده از مبادرت خود به اصلاح يا بدست آوردن داده يك شي اطلاع دارد، اما از اينكه شي چگونه اين عمليات را انجام مي‌دهد، اطلاعي ندارد. در برخي از موارد امكان دارد كلاسي به يك روش مبادرت به عرضه داده در محيط داخلي نمايد، در حاليكه همان داده را به روش مختلفي در اختيار سرويس‌گيرنده‌ها قرار مي‌دهد. براي مثال، فرض كنيد كلاس Clock زماني از روز را بصورت يك عضو داده time و از نوع int و private عرضه مي‌كند كه تعداد ثانيه‌هاي از نيمه‌شب را ذخيره مي‌سازد. با اين همه، زمانيكه سرويس‌گيرنده‌اي تابع عضو getTime از شي Clock را فراخواني مي‌كند، زمان برحسب ساعت، دقيقه و ثانيه در يك رشته با فرمت "HH:MM:SS" به وي برگشت داده مي‌شود. به همين ترتيب، فرض كنيد كلاس Clock يك تابع set بنام setTime تدارك ديده كه يك رشته پارامتري با فرمت "HH:MM:SS" دريافت مي‌نمايد. با استفاده از قابليت‌هاي عرضه شده در فصل هيجدهم، تابع setTime قادر به تبديل اين رشته به تعداد ثانيه‌ها است، كه تابع آن را در عضو داده private ذخيره سازد. همچنين تابع set مي‌تواند به بررسي اعتبار مقدار دريافتي پرداخته و اعتبار آن را تاييد يا لغو كند (مثلاً "12:30:43" معتبر بوده اما "42:85:70" معتبر نيست) . توابع set و get به سرويس‌گيرنده‌ امكان تعامل با يك شي را فراهم مي‌آورند، اما داده private (خصوصي) شي همچنان بصورت كپسوله (پنهان) و ايمن در خودش نگهداري مي‌شود.

همچنين توابع set و get يك كلاس مي‌توانند توسط ساير توابع عضو موجود در درون كلاس به منظور دستكاري كردن داده private كلاس بكار گرفته شوند، اگر چه اين توابع عضو مي‌تواند بصورت مستقيم به داده private دسترسي پيدا كنند. در برنامه شكل 5-3، توابع عضو getCourseName و setCourseName از نوع توابع عضو public هستند، از اينرو در دسترس‌گيرنده‌هاي كلاس و همچنين خود كلاس قرار دارند. تابع عضو displayMessage تابع عضو getCourseName را براي بدست آوردن مقدار عضو داده courseName براي نمايش آن، فراخواني مي‌كند، با اينكه خود displayMessage مي‌تواند بصورت مستقيم به courseName دسترسي پيدا كند. دسترسي به عضو داده از طريق تابع get آن، سبب ايجاد يك كلاس بهتر و پايدارتر مي‌شود (كلاسي كه نگهداري آن آسان بوده و كمتر از كار مي‌افتد). اگر تصميم به تغيير عضو داده courseName بگيريم، ساختار تعريف‌كننده displayMessage نيازمند اصلاح نخواهد بود، فقط بدنه توابع get و set كه مستقيماً عضو داده را دستكاري مي‌كنند نياز به تغيير خواهند داشت. براي مثال، فرض كنيد كه مي‌خواهيم نام دوره را در دو عضو داده عرضه كنيم، courseName (مثلاً "CS101" ) و courseTitle (مثلاً "Introduction to C++ Programming" ). هنوز هم تابع displayMessage مي‌تواند با يك فراخواني تابع عضو getCourseName نام كامل دوره را بدست آورده و بعنوان بخشي از پيغام خوش‌آمدگويي به نمايش در آورد. در اينحالت، تابع getCourseName نيازمند ايجاد و برگشت دادن يك رشته حاوي courseNumber  و بدنبال آن courseTitle است. در ادامه تابع عضو displayMessage عنوان كامل دوره "CS101 Introduction to C++ Programming" را به نمايش در خواهد آورد، چرا كه از تغييرات صورت گرفته بر روي اعضاي داده كلاس به دور مانده است. مزاياي فراخواني تابع set از يك تابع عضو ديگر كلاس به هنگام بحث اعتبارسنجي در بخش 10-3 بيشتر آشكار خواهد شد.

 

برنامه‌نويسي ايده‌ال

سعي كنيد هميشه ميزان تاثيرات در اعضاي داده كلاس را با استفاده از توابع set و get در سطح محلي نگهداري كنيد.

 

مهندسي نرم‌افزار

نوشتن برنامه‌هايي كه درك و نگهداري آنها آسان باشد، مهم است. تغييرات هميشه رخ مي‌دهند. بعنوان برنامه‌نويس بايد انتظار داشته باشيد كه كدهاي نوشته شده توسط شما زماني به تغيير نياز پيدا خواهند كرد.

 

مهندسي نرم‌افزار

طراح كلاس نيازي به تدارك ديدن توابع set يا get براي هر ايتم داده private ندارد، اين موارد فقط در صورت نياز و شرايط مقتضي در نظر گرفته شوند. اگر سرويسي براي كد سرويس‌گيرنده مناسب باشد، بايد آن سرويس‌ در واسط public كلاس وارد گردد.

دياگرام UML كلاس GradeBook با يك داده عضو و توابع set و get

شكل 6-3 حاوي دياگرام كلاس UML به روز شده براي نسخه‌اي از كلاس GradeBook بكار رفته در برنامه 5-3 است. اين دياگرام مبادرت به مدل‌سازي داده عضو courseName بعنوان يك صفت در بخش مياني كلاس كرده است. UML اعضاي داده را در ليستي كه در آن نام صفت، يك كولن و نوع صفت قرار گرفته عرضه مي‌كند. نوع UML صفت courseName ، نوع String است كه متناظر با string در C++ مي‌باشد. عضو داده courseName در C++ حالت private دارد و از اينرو در دياگرام كلاس با يك علامت منفي (-) در مقابل نام صفت مشخص شده است. علامت منفي در UML معادل با تصريح‌كننده دسترسي private در C++ است. كلاس GradeBook حاوي سه تابع عضو public است، از اينرو در ليست دياگرام كلاس اين سه عمليات در بخش تحتاني يا سوم جاي گرفته‌اند. بخاطر داريد كه نماد جمع (+) قبل نام هر عمليات نشان مي‌دهد كه عمليات در ‍ C++ حالت public دارد. عمليات setCourseName داراي يك پارامتر String بنام name است. در UML نوع برگشتي از يك عمليات با قرار دادن يك كولن و نوع برگشتي پس از پرانتزهاي نام عمليات مشخص مي‌شود. تابع عضو getCourseName از كلاس GradeBook (شكل 5-3) داراي نوع string برگشتي در C++  است، بنابر اين دياگرام كلاس نوع برگشتي String را در UML به نمايش درآورده است. توجه نمائيد كه عمليات‌هاي setCourseName و displayMessage مقداري برگشت نمي‌دهند (نوع برگشتي آنها void است)، از اينرو در دياگرام كلاس UML نوع برگشتي از پرانتزها براي آنها در نظر گرفته نشده است. UML از void همانند C++ در زمانيكه تابع مقداري برگشت نمي‌دهد، استفاده نمي‌كند.

 

 

 

 

شكل 6-3 | دياگرام كلاس UML براي كلاس GradeBook با يك صفت private براي courseName و عمليات‌هاي public براي توابع setCourseName ، getCourseName و d isplayMessage

 

 

 

 

 

 

7-3        مقداردهي اوليه شي‌ها با سازنده‌ها

همانطوري كه از بخش 6-3 بخاطر داريد، زمانيكه يك شي از كلاس GradeBook ايجاد شد (شكل 5-3) عضو داده آن يعني courseName بطور پيش‌فرض با يك رشته تهي مقداردهي اوليه شده بود. اگر بخواهيم به هنگام ايجاد يك شي از GradeBook نام دوره‌اي تدارك ديده شود، چه كاري بايد انجام داد؟ هر كلاسي كه اعلان مي‌كنيد مي‌تواند يك سازنده (constructor) داشته باشد كه مي‌توان با استفاده از آن مبادرت به مقداردهي اوليه يك شي از كلاس به هنگام ايجاد شي كرد. سازنده يك تابع عضو ويژه است كه بايستي همنام با نام كلاس تعريف شده باشد، از اينروست كه كامپايلر مي‌تواند آن را از ديگر توابع عضو كلاس تشخيص دهد. مهمترين تفاوت موجود مابين سازنده‌ها و توابع ديگر در اين است كه سازنده‌ها نمي‌تواند مقدار برگشت دهند، بنابر اين نمي‌توانند نوع برگشتي داشته باشند (حتي void ). معمولاً، سازنده‌ها بصورت public اعلان مي‌شود. غالباً كلمه "constructor" در برخي از نوشته‌ها بصورت خلاصه شده "ctor" بكار گرفته مي‌شود، كه استفاده از آن را ترجيح نداده‌ايم.

C++ نيازمند فراخواني يك سازنده براي هر شي است كه ايجاد مي‌شود، در چنين حالتي مطمئن خواهيم بود كه شي قبل از اينكه توسط برنامه بكار گرفته شود، بدرستي مقداردهي اوليه شده است. فراخواني سازنده به هنگام ايجاد شي، بصورت غيرصريح يا ضمني انجام مي‌شود. در هر كلاسي كه بصورت صريح سازنده‌اي را مشخص نكرده است، كامپايلر يك سازنده پيش‌فرض تدارك مي‌بيند، اين سازنده داراي پارامتر نمي‌باشد. براي مثال، زمانيكه خط 46 از برنامه شكل 5-3 يك شي GradeBook ايجاد مي‌كند، سازنده پيش‌فرض فراخواني مي‌شود، چرا كه در اعلان myGradeBook بطور صريح هيچ آرگومان سازنده‌اي مشخص نشده است. سازنده پيش‌فرض تدارك ديده شده از سوي كامپايلر يك شي GradeBook بدون هيچ گونه مقادير اوليه براي اعضاي داده شي ايجاد مي‌كند. [نكته: براي اعضاي داده كه شي‌هاي از ساير كلاس‌ها هستند، سازنده پيش‌فرض بصورت ضمني هر سازنده پيش‌فرض عضو داده را براي اطمينان از اينكه اعضاء داده به درستي مقداردهي اوليه شده‌اند، فراخواني مي‌كند. در واقع به اين دليل است كه عضو داده courseName از نوع رشته (در برنامه شكل 5-3) با يك رشته تهي مقداردهي اوليه شده است. سازنده پيش‌فرض براي كلاس string مبادرت به تنظيم يك مقدار رشته‌اي با رشته تهي مي‌كند، در بخش 3-10 مطالب بيشتري در ارتباط با مقداردهي اوليه اعضاي داده كه شي‌هاي از كلاس‌هاي ديگر هستند خواهيد آموخت.]

در مثال برنامه شكل 7-3، نام يك دوره را به هنگام ايجاد يك شي از GradeBook مشخص كرده‌ايم (خط 49 ). در اين مورد، آرگومان "CS101 Introduction to C++ Programming" به سازنده شي GradeBook ارسال مي‌شود (خطوط 17-20 ) و مبادرت به مقداردهي اوليه courseName مي‌نمايد. برنامه شكل 7-3 يك كلاس GradeBook اصلاح شده تعريف كرده كه حاوي يك سازنده با يك پارامتر رشته‌اي است كه نام دورة اوليه را دريافت مي‌كند.

1   // Fig. 3.7: fig03_07.cpp

2   // Instantiating multiple objects of the GradeBook class and using 

3   // the GradeBook constructor to specify the course name

4   // when each GradeBook object is created.

5   #include <iostream>

6   using std::cout;

7   using std::endl;

8    

9   #include <string> // program uses C++ standard string class

10 using std::string;

11  

12 // GradeBook class definition

13 class GradeBook

14 {

15 public:

16   // constructor initializes courseName with string supplied as argument

17    GradeBook( string name )

18    {

19       setCourseName( name );//call set function to initialize courseName

20    } // end GradeBook constructor

21  

22    // function to set the course name

23    void setCourseName( string name )

24    {

25       courseName = name; // store the course name in the object

26    } // end function setCourseName

27  

28    // function to get the course name

29    string getCourseName()

30    {

31       return courseName; // return object's courseName

32    } // end function getCourseName

33  

34    // display a welcome message to the GradeBook user

35    void displayMessage()

36    {

37       // call getCourseName to get the courseName

38       cout << "Welcome to the grade book for\n" << getCourseName() 

39          << "!" << endl;

40    } // end function displayMessage

41 private:

42    string courseName; // course name for this GradeBook

43 }; // end class GradeBook 

44  

45 // function main begins program execution

46 int main()

47 {

48    // create two GradeBook objects

49    GradeBook gradeBook1( "CS101 Introduction to C++ Programming" );

50    GradeBook gradeBook2( "CS102 Data Structures in C++" );

51  

52    // display initial value of courseName for each GradeBook

53    cout <<"gradeBook1 created for course:"<< gradeBook1.getCourseName()

54       <<"\ngradeBook2 created for course:"<< gradeBook2.getCourseName()

55       << endl;

56    return 0; // indicate successful termination

57 } // end main

gradeBook1 created for course : CS101 Introduction to C++ Programming

gradeBook2 created for course : CS102 Data Structures in C++

شكل 7-3 | نمونه‌سازي شي‌هاي مضاعف از كلاس GradeBook و استفاده از سازنده GradeBook براي مشخص كردن نام دوره به هنگام ايجاد هر شي GradeBook .

 

تعريف سازنده

در خطوط 17-20 برنامه شكل 7-3 يك سازنده براي كلاس GradeBook تعريف شده است. توجه كنيد كه سازنده داراي نام مشابه همانند كلاس خود يعني GradeBook است. يك سازنده توسط ليست پارامتري، داده مورد نياز براي انجام وظيفه خود را مشخص مي‌سازد. زمانيكه يك شي جديد ايجاد مي‌كنيد، اين داده را در درون پرانتزهاي قرار گرفته پس از نام شي قرار مي‌دهيد (همانند خطوط 49-50 ). خط 17 بر اين نكته دلالت دارد كه سازنده كلاس GradeBook داراي يك پارامتر رشته‌اي بنام name است. دقت كنيد كه در خط 17 نوع برگشتي مشخص نشده است، چرا كه سازنده‌ها نمي‌توانند مقدار برگشت دهند (حتي void ).

خط 19 در بدنه سازنده مبادرت به ارسال پارامتر name سازنده به تابع عضو setCourseName مي‌كند كه مقداري به عضو داده courseName تخصيص مي‌دهد. تابع عضو setCourseName (خطوط 23-26 ) فقط مبادرت به تخصيص پارامتر name خو د به عضو داده courseName مي‌كند، بنابر اين ممكن است تعجب كنيد كه چرا زحمت فراخواني setCourseName را در خط 19 به خود داده‌ايم، در حاليكه سازنده بطور مشخص عمليات تخصيص courseName=name را انجام مي‌دهد. در بخش 10-3، مبادرت به اصلاح setCourseName براي انجام عمليات اعتبارسنجي خواهيم كرد. در اين بخش است كه مزيت فراخواني setCourseName ا ز سازنده آشكار خواهد شد. توجه نمائيد كه در سازنده (خط 17 ) و هم تابع setCourseName (خط 23 ) از پارامتري بنام name استفاده شده است. مي‌توانيد از اسامي پارامتري يكسان در توابع مختلف استفاده كنيد چرا كه پارامترها حالت محلي براي هر تابع دارند و سبب تداخل با ديگري نمي‌شوند.

تست كلاس GradeBook

خطوط 46-57 از برنامه شكل 7-3 حاوي تعريف تابع main است كه مبادرت به تست كلاس GradeBook و اثبات مقداردهي اوليه شي GradeBook با استفاده از يك سازنده مي‌كند. خط 49 در تابع main اقدام به ايجاد و مقداردهي اوليه يك شي GradeBook بنام gradeBook1 مي‌كند. زماني كه اين خط از كد اجرا شود، سازنده GradeBook در خطوط 17-20 همراه با آرگومان "CS101 Introduction to C++ Programming" براي مقداردهي اوليه نام دوره gradeBook1 فراخواني مي‌شود، البته اين فراخواني بصورت ضمني و توسط C++ صورت مي‌گيرد. خط 50 تكرار اين فرآيند براي يك شي GradeBook  بنام gradeBook2 است، اين بار، آرگومان "CS102 Data Structures in C++" براي مقداردهي اوليه نام دوره gradeBook2 ارسال مي‌شود. خطوط 53-54 از تابع عضو هر شي getCourseName براي بدست آوردن اسامي دوره و نمايش اينكه آنها در زمان ايجاد مقداردهي اوليه شده‌اند، استفاده كرده‌اند. خروجي برنامه تاييد مي‌كند كه هر شي GradeBook از كپي عضو داده courseName  متعلق بخود نگهداري كرده است.

دو روش براي تدارك ديدن يك سازنده پيش‌فرض براي يك كلاس

به هر سازنده‌اي كه هيچ آرگوماني دريافت نكند، سازنده پيش‌فرض مي‌گويند. يك كلاس به يكي از دو روش زير سازندة پيش‌فرض بدست مي‌آورد:

1- كامپايلر بطور ضمني يك سازنده پيش‌فرض براي كلاسي كه سازنده‌اي براي آن تعريف نشده است، ايجاد مي‌كند. چنين سازنده‌اي مبادرت به مقداردهي اوليه اعضاي داده كلاس نمي‌كند، اما براي هر عضو داده كه شي از كلاس ديگري هستند، سازنده پيش‌فرض را فراخواني مي‌كند [نكته: معمولاً يك متغير مقداردهي نشده حاوي يك مقدار «اشغال» است (مثلاً يك متغير int مقداردهي نشده مي‌تواند حاوي 858993460 - باشد كه اين مقدار در بسياري از برنامه‌ها براي اين متغير غلط مي‌باشد).]

2- برنامه‌نويس بصورت صريح مبادرت به تعريف سازنده‌اي نمايد كه آرگوماني دريافت نمي‌كند. چنين سازنده‌اي شروع به مقداردهي اوليه مطابق‌ نظر برنامه‌نويس كرده و سازنده پيش‌فرض را براي هر داده عضو كه شي از يك كلاس ديگر است فراخواني مي‌كند.

اگر برنامه‌نويس مبادرت به تعريف يك سازنده با آرگومان نمايد، ديگر C++ بصورت ضمني يك سازنده پيش‌فرض براي آن كلاس ايجاد نخواهد كرد. توجه نمائيد كه براي هر نسخه از كلاس GradeBook در برنامه‌هاي 1-3، 3-3 و 5-3 كامپايلر بصورت ضمني يك سازنده پيش‌فرض تعريف كرده است.

 

اجتناب از خطا

بجز در مواردي كه نيازي به مقداردهي اوليه اعضاي داده كلاس ضروري نيست (تقريباً هيچ وقت)، يك سازنده پيش‌فرض در نظر بگيريد كه حتماً اعضاي داده كلاس را با مقادير مناسب به هنگام ايجاد هر شي جديد مقداردهي اوليه نمايد.

 

مهندسي نرم‌افزار

اعضاي داده مي‌توانند توسط سازنده يك كلاس مقداردهي اوليه شوند يا پس از ايجاد شي تنظيم گردند. با اين وجود، از منظر مهندسي نرم‌افزار بهتر خواهد بود تا يك شي بطور كامل و قبل از اينكه سرويس‌گيرنده‌اي مبادرت به فراخواني توابع عضو آن شي نمايد، مقداردهي اوليه شده باشد. بطور كلي، نبايستي فقط متكي به كد سرويس‌گيرنده باشيد كه بدقت و بدرستي يك شي را مقداردهي نمايد.

افزودن سازنده به دياگرام كلاس UML كلاس GradeBook

دياگرام كلاس UML در شكل 8-3 مبادرت به مدل كردن كلاس GradeBook برنامه 7-3 كرده است، كه داراي يك سازنده با پارامتر name از نوع رشته است (نوع String در UML ). همانند عمليات‌ ها ، UML مبادرت به مدل‌سازي سازنده‌ها در بخش سوم كلاس در دياگرام كلاس مي‌كند. براي تمايز قائل شدن مابين يك سازنده از يك عمليات كلاس، UML مبادرت به قرار دادن كلمه "constructor" مابين گيومه (« و ») قبل از نام سازنده مي‌كند. قرار دادن ليست سازنده كلاس قبل از ديگر عمليات‌ها در بخش سوم امري رايج است.

 



شكل 8-3 | دياگرام كلاس
UML نشان مي‌دهد كه كلاس GradeBook داراي يك سازنده با پارامتر name از نوع String است.

 

تشکرات از این پست
دسترسی سریع به انجمن ها