آموزش ++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"
مابين گيومه (« و ») قبل از نام سازنده ميكند. قرار دادن ليست سازنده كلاس قبل از ديگر عملياتها در بخش سوم امري رايج است.