• توجه: در صورتی که از کاربران قدیمی ایران انجمن هستید و امکان ورود به سایت را ندارید، میتوانید با آیدی altin_admin@ در تلگرام تماس حاصل نمایید.

++ C

!MAHSA!

کاربر ويژه
تعريف کلاس

تعريف کلاس


توسط کلاس ها می توان اشيايی را با خصوصيات ( که توسط عناصر نمايش داده می شوند ) و اعمال ( که توسط توابع مشخص می شوند ) مدلسازی کرد . کلاسها همانند ساختارها تعريف می شوند با اين تفاوت که در کلاسها توابع نيز می توانند به عنوان عنصر کلاس باشند . يک کلاس توسط کلمه class تعريف می شود . به تعريف کلاس زير توجه کنيد :

class Time {
public:
Time();
void setTime( int, int, int );
void printUniversal();
void printStandard();

private:
int hour;
int minute;
int second;
};
تعريف کلاس Time با کلمه class شروع می شود و بدنه کلاس با {} مشخص می شود و در انتها تعريف کلاس با يک نقطه ويرگول (;) به پايان می رسد. همانند ساختار Time که در مبحث قبل ايجاد کرديم کلاس Time دارای سه عنصر از نوع عدد صحيح با نامهایhour وminute وsecond می باشد ساير اجزای کلاس جديد می باشد که به بررسی آنها می پردازيم . برچسبهای :public و :private معرفهای دستيابی به عنصر ( عضو ) ناميده می شوند . هر عضو داده ای يا تابع عضوی که بعد از public و قبل از private تعريف می شود ، در هر جايی که برنامه به شيئی از نوع Time دسترسی دارد قابل دستيابی می باشد. هر عضو داده ای يا تابعی که بعد از private تعريف می شود تنها برای توابع عضو کلاس قابل دستيابی می باشد و به طور مستقيم از داخل برنامه قابل دسترسی نمی باشند . تأکيد می شود که بعد از معرفهای دستيابی به اعضاء يک علامت دو نقطه :)) قرار می گيرد . ضمناً اين معرفها را در تعريف کلاسها ، چندين بار و به هر ترتيبی می توان به کار برد ولی توصيه می شود که برای خوانايی برنامه هر يک از اين معرفها را تنها يکبار به کار ببريد و ابتدا اعضای عمومی ( public ) را قرار دهيد . تعريف کلاس Time شامل پيش تعريف چهار تابع printUniversal , setTime ,Time و printStandard می شود که اين توابع ، اعضای عمومی کلاس Time می باشند ، چون بعد از publice تعريف شده اند .

توجه داشته باشيد که تابع عضوی همنام با کلاس وجود دارد که اين تابع ، تابع سازنده ناميده می شود . تابع سازنده ، تابع عضو بخصوصی است که مقادير اوليه اعضای داده ای را تعيين می کند . هنگامی که برنامه شيئی از کلاسی را ايجاد می کند ، تابع سازنده به طور خودکار فراخوانی می گردد. توجه داشته باشيد که تابع سازنده نبايد مقداری را به عنوان خروجی خود برگرداند .

همانند ساختارها ، هنگامی که کلاس Time تعريف گرديد می توان اشيايی را از نوع کلاس Time ايجاد کرد . به دستورات زير توجه کنيد :

Time sunset;
Time arrayofTime [5];
Time *timeptr = & sunset;
توسط دستورات فوق sunset متغيری از نوع کلاس Time تعريف می شود. arrayofTime آرايه ای شامل 10 عنصر از نوع کلاس Time می باشد و timePtr اشاره گری به خانه ای از حافظه با نام sunset از نوع کلاس Time تعريف می گردد.

در برنامه زير کلاس Time مورد استفاده قرار گرفته است و توابع عضو آن تعريف گرديده اند. در اين برنامه شيئی از نوع کلاس Time بانام t ايجاد شده است. هنگامی که اين شیء ايجاد می شود تابع سازنده Time به طور خودکار فراخوانی می شود ومقدار هر يک از اعضای داده (hour , minute , secound) را عدد صفر قرار می دهد. پس از ايجاد شدن شیء t توابع عضو printUniversal و printStandard فراخوانی می شوند تا با چاپ زمان توسط اين دو تابع از مقدار دهی اوليه به شیء t اطمينان حاصل کنيم. سپس با فراخوانی تابع setTime ، شیء t مقدار دهی می شود و سپس مجددا مقدار اين شیء به دو روش چاپ می گردد. هنگامی که با فراخوانی مجدد تابع عضو setTime سعی در مقدار دهی نادرست به شیء t می کنيم ، تابع عضو setTime از اين کار جلوگيری می کند.

#include <iostream.h>

class Time {
public:
Time();
void setTime( int, int, int );
void printUniversal();
void printStandard();

private:
int hour; // 0 - 23 (24-hour clock format)
int minute; // 0 - 59
int second; // 0 - 59
}

Time::Time()
{
hour = minute = second = 0;
}

void Time::setTime( int h, int m, int s )
{
hour = ( h >= 0 && h < 24 ) ? h : 0;
minute = ( m >= 0 && m < 60 ) ? m : 0;
second = ( s >= 0 && s < 60 ) ? s : 0;
}

void Time::printUniversal()
{
cout << (hour<10 ? "0":"") << hour << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second;
}


void Time::printStandard()
{
cout << ( ( hour == 0 || hour == 12 ) ?
12 : hour % 12 ) << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second
<< (hour < 12 ? " AM" : " PM" );
}

int main()
{
Time t;
cout << "The initial universal time is ";
t.printUniversal(); // 00:00:00

cout << "\nThe initial standard time is ";
t.printStandard(); // 12:00:00 AM

t.setTime( 13, 27, 6 ); // change time

cout << "\n\nUniversal time after setTime is ";
t.printUniversal(); // 13:27:06

cout << "\nStandard time after setTime is ";
t.printStandard(); // 1:27:06 PM

t.setTime( 99, 99, 99 ); // attempt invalid settings

cout << "\n\nAfter attempting invalid settings:"
<< "\nUniversal time: ";
t.printUniversal(); // 00:00:00

cout << "\nStandard time: ";
t.printStandard(); // 12:00:00 AM
cout << endl;

return 0;
}

خروجی برنامه فوق به صورت زير می باشد:

The initial universal time is 00:00:00
The initial standard time is 12:00:00 AM

Universal time after setTime is 13:27:06
Standard time after setTime is 1:27:06 PM

After attempting invalid settings:
Universal time: 00:00:00
Standard time: 12:00:00 AM
همانطور که در برنامه فوق مشاهده می کنيد هنگام تعريف کلاس تنها پيش تعريف توابع عضو آورده شده است و خود توابع در خارج از کلاس تعريف شده اند. برای اين کار قبل از نام تابع ، نام کلاس را همراه با عملگر تفکيک دامنه:):) قرار می دهيم :

void Time::printUniversal()
{
cout << (hour<10 ? "0":"") << hour << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second;
}
 

!MAHSA!

کاربر ويژه
دسترسی به عناصر ساختار

دسترسی به عناصر ساختار


برای دستيابی به عناصر يک ساختار از عملگر نقطه (.) و يا عملگر پيکان (<-) استفاده می کنيم . عملگر نقطه برای دستيابی به عنصر يک ساختار از طريق نام يک متغير ، مورد استفاده قرار می گيرد ، به عنوان مثال به دستور زير توجه کنيد :

timeObject.hour = 13;
timeObject.minute = 33;
timeObject.second = 20;
دستور فوق عناصر hour و minute و second متغيری از نوع Time را به ترتيب با مقادير 13 و 33 و 20 مقدار دهی می کند و دستور زير :

cout << timeObject.hour;
برای چاپ کردن عنصرhour از متغيرtime Object به کار می رود . عملگر پيکان که از يک علامت منفی (-) و يک علامت بزرگتر (<) بدون فاصله بين اين دو علامت تشکيل شده است ، برای دستيابی به عنصر يک ساختار از طريق اشاره گر مورد استفاده قرار می گيرد .

cout << timePtr -> hour;
دستور فوق برای چاپ کردن عنصر hour از متغير timeObject توسط اشاره گر timePtr به کار رفته است . به جای استفاده از timePtr -> hour برای دستيابی به عنصر hour می توان از (*timePtr).hour استفاده کرد . توجه داشته باشيد که استفاده از پرانتز در کنار timePtr* الزامی می باشد . به عنوان مثال از دستور زير نيز برای چاپ عنصر hour از متغير timeObject توسط اشاره گر timePtr می توان استفاده کرد .

cout << (*timePtr).hour;
در برنامه زير ساختار Time مورد استفاده قرار گرفته است و توسط روش ارسال آرگومان با ارجاع به توابع ، شیء dinnerTime که از نوع ساختار Time می باشد به توابع printUniversal و printStandard ارسال شده است.

#include <iostream.h>

struct Time {
int hour; // 0-23 (24-hour clock format)
int minute; // 0-59
int second; // 0-59
};

void printUniversal( const Time & );
void printStandard( const Time & );

int main()
{
Time dinnerTime;

dinnerTime.hour = 18;
dinnerTime.minute = 30;
dinnerTime.second = 0;

cout << "Dinner will be held at ";
printUniversal( dinnerTime );
cout << " universal time,\nwhich is ";
printStandard( dinnerTime );
cout << " standard time.\n";

dinnerTime.hour= 29; //set hour to invalid value
dinnerTime.minute= 73; //set minute to invalid value

cout << "\nTime with invalid values: ";
printUniversal( dinnerTime );
cout << endl;

return 0;
}

void printUniversal( const Time &t )
{
cout << (t.hour<10 ? "0":"") << t.hour << ":"
<< (t.minute<10 ? "0":"") << t.minute << ":"
<< (t.second<10 ? "0":"") << t.second;
}


void printStandard( const Time &t )
{
cout << ( ( t.hour == 0 || t.hour == 12 ) ?
12 : t.hour % 12 ) << ":"
<< (t.minute<10 ? "0":"") << t.minute << ":"
<< (t.second<10 ? "0":"") << t.second
<< ( t.hour < 12 ? " AM" : " PM" );
}
خروجی برنامه فوق به صورت زير می باشد:

Dinner will be held at 18:30:00 universal time,
which is 6:30:00 PM standard time.

Time with invalid values: 29:73:00
در برنامه فوق مشاهده کرديد ، امکان مقدار دهی نادرست به شيئی از نوع ساختارTime وجود دارد. اما در مبحث بعدی در کلاسها خواهيد ديد که می توان مقدار دهی به اعضا را کنترل کرد.
 

!MAHSA!

کاربر ويژه
حوزه کلاس و دسترسی به اعضای کلاس

حوزه کلاس و دسترسی به اعضای کلاس


اعضای داده يک کلاس (متغيرهايی که در کلاس تعريف شده اند ) و توابع عضو کلاس ( توابعی که در کلاس تعريف گرديده ا ند ) به حوزه اين کلاس متعلق می باشند و توابعی که عضو کلاس نمی باشند، دارای حوزه فايل می باشند.

در حوزه يک کلاس ، همه اعضای کلاس توسط توابع عضو کلاس قابل دسترسی می باشند و می توان توسط نامشان مستقيما به آنها مراجعه کرد. در بيرون از حوزه کلاس برای دسترسی به اعضای کلاس از عملگر نقطه (.) و يا عملگر پيکان (<-) استفاده می کنيم. عملگر هايی که برای دسترسی به اعضای کلاس مورد استفاده قرار می گيرند همانند عملگر های دسترسی به عناصر ساختارها می باشند. عملگر نقطه (.) برای دستيابی به عضو يک کلاس از طريق نام يک متغير و عملگر پيکان (<-) ، برای دستيابی به عضو يک کلاس از طريق اشاره گر مورد استفاده قرار می گيرد .

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

متغيرهايی که در يک تابع عضو کلاس تعريف شده اند، دارای حوزه تابع می باشند، یعنی فقط برای همان تابع شناخته شده اند. اگر در تابع عضوی، متغيری همنام با نام متغيری که دارای حوزه کلاس است تعريف شده باشد، در اين صورت متغيری که دارای حوزه کلاس می باشد در داخل تابع به طور مستقيم نمی توان رجوع کرد و برای دسترسی به آن بايد عملگر تفکيک حوزه :):) را قبل از نام متغير قرار داد.

در برنامه زير يک کلاس ساده به نام Count با يک عضو داده عمومی از نوع int به نام x و يک تابع عضو عمومی به نام print ، تعريف شده است. در اين برنامه دو شیء از نوع کلاس Count به نامهای counter و countPtr ( اشاره گری به يک شیء Count ) تعريف شده اند که متغيرcountPtr به counter اشاره داده شده است. لازم به ذکر است که عضو داده x فقط برای اين به صورت عمومی تعريف شده که نحوه دسترسی به اعضای عمومی را نشان دهيم. همان طور که قبلا ذکر کرديم، اعضای داده ای معمولاً به صورت خصوصی اعلان می شوند . به برنامه زير توجه کنيد :

#include <iostream.h>

class Count {

public:
int x;

void print()
{
cout << x << endl;
}
};

void main()
{
Count counter;
Count *counterPtr = &counter;

cout << "Assign 1 to x and print "
<< "using the object's name: ";
counter.x = 1;
counter.print();

cout << "Assign 3 to x and print using a pointer: ";
counterPtr->x = 3;
counterPtr->print();
}
خروجی برنامه فوق به صورت زير می باشد:

Assign 1 to x and print using the object's name: 1
Assign 3 to x and print using a pointer: 3
 

!MAHSA!

کاربر ويژه
کنترل دسترسی به اعضا

کنترل دسترسی به اعضا


معرفهای دسترسی به اعضا یعنی public و private ، نحوه دسترسی به اعضای داده و توابع عضو کلاس را کنترل می کنند. حالت پیش فرض برای دسترسی به اعضای داده و توابع عضو در کلاس private می باشد، لذا همه اعضایی که بعد از تعيين نام کلاس و قبل از اولین برچسب معرف آمده اند، خصوصی در نظر گرفته می شوند. پس از هر معرف، حالتی که معرف مربوطه تعيين می کند، به همه اعضا تا برچسب بعدی یا آکلاد بسته مربوط به پایان تعریف کلاس اعمال می شود. برچسبهای public و private را می توان چندین بار در تعریف کلاس به کار برد، اما این کار معمول نمی باشد و باعث پيچيدگی کد برنامه می شود. اعضای خصوصی یک کلاس فقط از طریق توابع عضو آن کلاس قابل دسترسی می باشند در مقابل اعضای عمومی کلاس توسط هر تابعی در برنامه قابل دسترسی می باشند.

برنامه زير نشان می دهد که اعضای خصوصی کلاس، خارج از کلاس قابل دسترسی نمی باشند :

#include <iostream.h>

class Time {
public:
Time();
void setTime( int, int, int );
void printUniversal();
void printStandard();

private:
int hour; // 0 - 23 (24-hour clock format)
int minute; // 0 - 59
int second; // 0 - 59
}

Time::Time()
{
hour = minute = second = 0;
}

void Time::setTime( int h, int m, int s )
{
hour = ( h >= 0 && h < 24 ) ? h : 0;
minute = ( m >= 0 && m < 60 ) ? m : 0;
second = ( s >= 0 && s < 60 ) ? s : 0;
}

void Time::printUniversal()
{
cout << (hour<10 ? "0":"") << hour << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second;
}


void Time::printStandard()
{
cout << ( ( hour == 0 || hour == 12 ) ?
12 : hour % 12 ) << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second
<< (hour < 12 ? " AM" : " PM" );
}

int main()
{
Time t; // create Time object

t.hour = 7; // error: 'Time::hour' is not accessible

// error: 'Time::minute' is not accessible
cout << "minute = " << t.minute;

return 0;
}

هنگام کامپايل کردن برنامه فوق با پيغام خطايی مبنی بر اينکه عضو خصوصی قابل دسترسی نمی باشد، مواجه می شويم .

Error in line 50: 'Time::hour' is not accessible
Error in line 53: 'Time::minute' is not accessible
 

!MAHSA!

کاربر ويژه
سازنده ها

سازنده ها


هنگامی که شیئی از یک کلاس ساخته می شود، اعضای داده آن را می توان با تابع سازنده آن کلاس مقدار دهی اوليه کرد. تابع سازنده ، تابع عضو بخصوصی است که مقادير اوليه اعضای داده ای را تعيين می کند. هنگامی که برنامه شيئی از کلاسی را ايجاد می کند ، تابع سازنده به طور خودکار فراخوانی می گردد. توجه داشته باشيد که تابع سازنده نبايد مقداری را به عنوان خروجی خود برگرداند.

سازنده به کار رفته در برنامه مبحث تعريف کلاس مقادیر اولیه minute ، hour و second را برابر با صفر قرار داد. اما توابع سازنده می توانند دارای آرگومان باشند، که معمولا اين آرگومان ها برای مقدار دهی اوليه به شيئی از نوع کلاس مربوطه به کار می روند. از آنجا که هنگام ايجاد شیء، تابع سازنده به طور خودکار فراخوانی می شود، اعضای داده ای کلاس را توسط آرگومان های در يافتی خود، می تواند مقدار دهی اوليه کند. مقادیر اولیه یک کلاس را می توان هنگام تعريف شیئی از آن کلاس، درون پرانتزهایی که در سمت راست نام شیء و پیش از نقطه ویرگول می آیند قرار داد. این مقادیر اولیه به عنوان آرگومان به تابع سازنده کلاس ارسال می گردند. به برنامه زير توجه کنيد:

#include <iostream.h>

class CRectangle {
int width, height;
public:
CRectangle (int,int);
int area (void) {return (width*height);}
};

CRectangle::CRectangle (int a, int b) {
width = a;
height = b;
}

int main () {
CRectangle rect (3,4);
CRectangle rectb (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
}
خروجی برنامه فوق به صورت زير می باشد:

rect area: 12
rectb area: 30
سازنده ها را برای اينکه مقدار دهی اوليه به اعضای داده ای کلاس به صورتهای مختلف امکان پذیر باشد، می توان گرانبار کرد. به نحوه گرانبار کردن تابع سازنده، در برنامه زير توجه نماييد:

#include <iostream.h>

class CRectangle {
int width, height;
public:
CRectangle ();
CRectangle (int,int);
int area (void) {return (width*height);}
};

CRectangle::CRectangle () {
width = 5;
height = 5;
}

CRectangle::CRectangle (int a, int b) {
width = a;
height = b;
}

int main () {
CRectangle rect (3,4);
CRectangle rectb;
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
}
خروجی برنامه فوق به صورت زير می باشد:

rect area: 12
rectb area: 25
سازنده ها می توانند دارای آرگومان های پیش فرض نيز باشند. در برنامه زير در پيش تعريف تابع سازنده time و برای هر یک از آرگومانها مقدار پیش فرض صفر در نظر گرفته شده است. با مشخص کردن مقدار پيش فرض برای آرگومانهای تابع سازنده، حتی اگر هنگام فراخوانی تابع سازنده مقداری نيز برای آن تعيين نگردد، مقادير پيش فرض به کار گرفته می شود. اين کار تضمين می نمايد که اعضاء کلاس همواره مقادير درستی را در خود نگهداری می کنند.

#include <iostream.h>

class Time {
public:
Time( int = 0, int = 0, int = 0);
void setTime( int, int, int );
void printUniversal();
void printStandard();

private:
int hour; // 0 - 23 (24-hour clock format)
int minute; // 0 - 59
int second; // 0 - 59
}

Time::Time( int hr, int min, int sec )
{
setTime( hr, min, sec );
}

void Time::setTime( int h, int m, int s )
{
hour = ( h >= 0 && h < 24 ) ? h : 0;
minute = ( m >= 0 && m < 60 ) ? m : 0;
second = ( s >= 0 && s < 60 ) ? s : 0;
}

void Time::printUniversal()
{
cout << (hour<10 ? "0":"") << hour << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second;
}


void Time::printStandard()
{
cout << ( ( hour == 0 || hour == 12 ) ?
12 : hour % 12 ) << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second
<< (hour < 12 ? " AM" : " PM" );
}


void main()
{
Time t1; // all arguments defaulted
Time t2( 2 ); // minute and second defaulted
Time t3( 21, 34 ); // second defaulted
Time t4( 12, 25, 42 ); // all values specified
Time t5( 27, 74, 99 ); // all bad values specified

cout << "Constructed with:\n\n"
<< "all default arguments:\n ";
t1.printUniversal(); // 00:00:00
cout << "\n ";
t1.printStandard(); // 12:00:00 AM

cout << "\n\nhour specified;";
<< " default minute and second:\n ";
t2.printUniversal(); // 02:00:00
cout << "\n ";
t2.printStandard(); // 2:00:00 AM

cout << "\n\nhour and minute specified;";
<< " default second:\n ";
t3.printUniversal(); // 21:34:00
cout << "\n ";
t3.printStandard(); // 9:34:00 PM

cout << "\n\nhour, minute, and second specified:\n ";
t4.printUniversal(); // 12:25:42
cout << "\n ";
t4.printStandard(); // 12:25:42 PM

cout << "\n\nall invalid values specified:\n ";
t5.printUniversal(); // 00:00:00
cout << "\n ";
t5.printStandard(); // 12:00:00 AM
cout << endl;

}

خروجی برنامه فوق به صورت زير می باشد:

Constructed with:

all default arguments:
00:00:00
12:00:00 AM

hour specified; default minute and second:
02:00:00
2:00:00 AM

hour and minute specified; default second:
21:34:00
9:34:00 PM

hour, minute, and second specified:
12:25:42
12:25:42 PM

all invalid values specified:
00:00:00
12:00:00 AM
در برنامه فوق پنج شیء از کلاس Time ايجاد شده اند که t1 هنگام فراخوانی سازنده از هر آرگومان پیش فرض استفاده کرده است، t2 یک آرگومان، t3 دو آرگومان، t4 سه آرگومان را برای سازنده خود مشخص کرده اند و t5 آرگومانهایی با مقادير غير مجاز را مشخص کرده است. ضمنا برای بررسی مجاز بودن آرگومان های دريافت شده توسط تابع سازنده، اين تابع ، setTime را با آرگومانهايی که دريافت کرده است فراخوانی می کند تا اطمينان حاصل شود که hour و minute و second با مقادير درستی مقدار دهی می شوند.
 

!MAHSA!

کاربر ويژه
نابود کننده ها

نابود کننده ها


نابودکننده همانند سازنده تابع عضو ويژه ای از کلاس و همنام با کلاس می باشد و با کاراکتر (~) شروع می گردد.

تابع نابودکننده یک کلاس هنگامی فراخوانده می شود که شیئی از آن کلاس نابود شود (مثلا وقتی که برنامه در حال اجرا، حوزه ای را که در آن، شیئی از آن کلاس نمونه سازی شده ترک می کند). خود نابودکننده شی را نابود نمی کند، بلکه امور خانه داری مربوط به پایان دهی را پیش از آن که سیستم، حافظه شی را باز پس گیرد و این حافظه برای اشیای بعدی آزاد شود، انجام می دهد.

نابودکننده هیچ پارامتری نمی گیرد و هیچ مقداری باز نمی گرداند. یک کلاس فقط یک نابودکننده می تواند داشته باشد. به عبارت دیگر گرانبار کردن نابودکننده مجاز نیست. در قطعه برنامه زير نحوه تعريف يک نابود کننده را می بينيد:

class CreateAndDestroy {

public:
CreateAndDestroy();
~CreateAndDestroy();

private:
int objectID;
};

CreateAndDestroy::CreateAndDestroy(int objectNumber)
{
objectID = objectNumber;
cout << "Object "<<objectID<<" constructor runs\n";
}


CreateAndDestroy::~CreateAndDestroy()
{
cout << "Object "<<objectID<<" destructor runs\n";
}
 

!MAHSA!

کاربر ويژه
فراخوانی سازنده ها و نابودکننده ها

فراخوانی سازنده ها و نابودکننده ها


سازنده ها و نابودکننده ها به طور خودکار فراخوانی می شوند. ترتیب فراخوانی این توابع بستگی به ترتیب ورود و ترک حوزه ای که اشیا در آن تعريف شده اند، دارد. به برنامه زير توجه کنيد:

#include <iostream.h>

class CreateAndDestroy {
public:
CreateAndDestroy( int, char * );
~CreateAndDestroy();

private:
int objectID;
char *message;
};

CreateAndDestroy::CreateAndDestroy(
int objectNumber, char *messagePtr )
{
objectID = objectNumber;
message = messagePtr;

cout << "Object " << objectID << " constructor runs "
<< message << endl;
}

CreateAndDestroy::~CreateAndDestroy()
{
cout << ( objectID == 1 || objectID == 6 ? "\n" : "" );

cout << "Object " << objectID << " destructor runs "
<< message << endl;
}

void create( void );

CreateAndDestroy first(1,"(global before main)");

void main()
{
cout << "\nMAIN FUNCTION: EXECUTION BEGINS" << endl;

CreateAndDestroy second(2,"(local automatic in main)");

create(); // call function to create objects

cout << "\nMAIN FUNCTION: EXECUTION RESUMES" << endl;

CreateAndDestroy third(3,"(local automatic in main)");

cout << "\nMAIN FUNCTION: EXECUTION ENDS" << endl;
}

void create( void )
{
cout << "\nCREATE FUNCTION: EXECUTION BEGINS" << endl;

CreateAndDestroy fourth(4,"(local automatic in create)");

cout << "\nCREATE FUNCTION: EXECUTION ENDS" << endl;
}
خروجی برنامه فوق به صورت زير می باشد:

Object 1 constructor runs (global before main)

MAIN FUNCTION: EXECUTION BEGINS
Object 2 constructor runs (local automatic in main)

CREATE FUNCTION: EXECUTION BEGINS
Object 4 constructor runs (local automatic in create)

CREATE FUNCTION: EXECUTION ENDS
Object 4 destructor runs (local automatic in create)

MAIN FUNCTION: EXECUTION RESUMES
Object 3 constructor runs (local automatic in main)

MAIN FUNCTION: EXECUTION ENDS
Object 3 destructor runs (local automatic in main)
Object 2 destructor runs (local automatic in main)

Object 1 destructor runs (global before main)
 

!MAHSA!

کاربر ويژه
اشياء ثابت و توابع عضو ثابت

اشياء ثابت و توابع عضو ثابت


همانطور که می دانيد کلمه const از تغيير بر روی متغيری که بعد از اين کلمه قرار می گيرد جلوگيری می کند. برای ايجاد يک شیء ثابت و غير قابل تغييرنيز می توان از const استفاده کرد.

const Time noon(12, 0, 0);
دستور فوق شيء ثابتی از نوع Time با نام noon ايجاد می کند و با 12 ظهر آنرا مقدار دهی می کند که اين مقدار قابل تغيير نمی باشد.

کامپايلر ++C اجازه فراخوانی توابع عضو را برای اشياء ثابت نمی دهد مگر اينکه توابع عضو نيز خودشان به صورت const تعريف شده باشند. برای اينکه تابع عضوی به صورت const تعريف شود، بايد هم در پيش تعريف تابع و هم در تعريف تابع از کلمه const بعد از ليست آرگومان های تابع استفاده کنيم.

int Time::getHour() const
{
return hour;
}
دستور فوق تابع عضو getHour از کلاس Time را به صورت ثابت تعريف می کند.

نکته:

تابع عضوی که يک عضو داده از شيئی را تغيير می دهد، اگر به صورت ثابت تعريف شود، باعث وقوع خطا می گردد.
تابع عضوی که تابع عضو غير ثابت ديگری را فراخوانی می کند، اگر به صورت ثابت تعريف شود، باعث وقوع خطا می گردد.
فراخوانی يک تابع عضو غير ثابت از شيئی که به صورت ثابت تعريف شده است، باعث ايجاد خطا می گردد.
يک تابع عضو غير ثابت را می توان با نسخه غير ثابتی از آن گرانبار نمود. اينکه کدام تابع عضو گرانبار شده فراخوانی شود، توسط کامپايلر و بر اساس ثابت بودن يا نبودن شیء تعيين می گردد.
توجه داشته باشيد از آنجا که سازنده ها و نابود کننده ها نياز به تغيير اشياء دارند نبايد آنها را به صورت ثابت تعريف نمود و اگر کلمه const برای آنها به کار رود و به صورت ثابت تعريفشان کنيم يک پيغام خطا دريافت خواهيم کرد.
سازنده ها در اشياء ثابت می توانند تابع عضو غير ثابتی را فراخوانی کنند.
در برنامه زير دو شیء نوع Time ايجاد شده اند که يکی ثابت و ديگری غير ثابت می باشد. در اين برنامه تابع عضو setHour و تابع عضو printStandard که به صورت توابع عضو غير ثابت می باشند، هنگام فراخوانی همانطور که در نکته 2 ذکر شد باعث وقوع خطا می گردند. برنامه زير نکات گفته شده در اين مبحث را روشن می سازد:

#include <iostream.h>

class Time {
public:
Time( int = 0, int = 0, int = 0);
void setTime( int, int, int );

void setHour( int );
void setMinute( int );
void setSecond( int );

int getHour() const;
int getMinute() const;
int getSecond() const;

void printUniversal() const;
void printStandard();

private:
int hour; // 0 - 23 (24-hour clock format)
int minute; // 0 - 59
int second; // 0 - 59
}

Time::Time( int hour, int minute, int second )
{
setTime( hour, minute, second );
}

void Time::setTime( int hour, int minute, int second )
{
setHour( hour );
setMinute( minute );
setSecond( second );
}

void Time::setHour( int h )
{
hour = ( h >= 0 && h < 24 ) ? h : 0;
}

void Time::setMinute( int m )
{
minute = ( m >= 0 && m < 60 ) ? m : 0;
}

void Time::setSecond( int s )
{
second = ( s >= 0 && s < 60 ) ? s : 0;
}

int Time::getHour() const
{
return hour;
}

int Time::getMinute() const
{
return minute;
}

int Time::getSecond() const
{
return second;
}

void Time::printUniversal() const
{
cout << (hour<10 ? "0":"") << hour << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second;
}

void Time::printStandard()
{
cout << ( ( hour == 0 || hour == 12 ) ?
12 : hour % 12 ) << ":"
<< (minute<10 ? "0":"") << minute << ":"
<< (second<10 ? "0":"") << second
<< (hour < 12 ? " AM" : " PM" );
}

int main()
{
Time wakeUp( 6, 45, 0 ); // non-constant object
const Time noon( 12, 0, 0 ); // constant object

// OBJECT MEMBER FUNCTION
wakeUp.setHour( 18 ); // non-const non-const

noon.setHour( 12 ); // const non-const

wakeUp.getHour(); // non-const const

noon.getMinute(); // const const
noon.printUniversal(); // const const

noon.printStandard(); // const non-const

return 0;
}

پيغام های خطای برنامه فوق به صورت زير می باشد:

Warning W8037 100: Non-const function
Time::setHour(int)
called for const object in function main()
Warning W8037 107: Non-const function
Time::printStandard()
called for const object in function main()
*** 2 errors in Compile ***
 

!MAHSA!

کاربر ويژه
ترکيب : اشياء به عنوان اعضای کلاس ها

ترکيب : اشياء به عنوان اعضای کلاس ها


شيء AlarmClock (زنگ ساعت) بايد بداند که چه وقت به صدا در آيد، پس بهتر است شيئی از نوع Time را به عنوان يکی از اعضای خود داشته باشد. چنين قابليتی در کلاس ها ترکيب ناميده می شود. يعنی يک کلاس می تواند کلاس يا کلاسهای ديگری را به عنوان يکی از اعضای خود داشته باشد.

با روش ارسال آرگومانها به تابع سازنده هنگام ايجاد شیء آشنا می باشيم. در اين مبحث روش ارسال آرگومان به تابع سازنده شیء عضو کلاس و مقدار دهی اوليه به آن را بررسی می کنيم. برای آشنايی با اين روش به برنامه زير توجه کنيد:

#include <iostream.h>
#include <string.h>

class Date {

public:
Date( int = 1, int = 1, int = 1900 );
void print() const;
~Date();

private:
int month; // 1-12 (January-December)
int day; // 1-31 based on month
int year; // any year

int checkDay( int ) const;
};

Date::Date( int mn, int dy, int yr )
{
if ( mn > 0 && mn <= 12 )
month = mn;
else {
month = 1;
cout << "Month " << mn
<< " invalid. Set to month 1.\n";
}

year = yr;
day = checkDay( dy );

cout << "Date object constructor for date ";
print();
cout << endl;
}


void Date::print() const
{
cout << month << '/' << day << '/' << year;
}

Date::~Date()
{
cout << "Date object destructor for date ";
print();
cout << endl;
}

int Date::checkDay( int testDay ) const
{
static const int daysPerMonth[ 13 ] =
{ 0,31,28,31,30,31,30,31,31,30,31,30,31 };


if ( testDay > 0 && testDay <= daysPerMonth[ month ] )
return testDay;

if ( month == 2 && testDay == 29 &&
( year % 400 == 0 ||
( year % 4 == 0 && year % 100 != 0 ) ) )
return testDay;

cout << "Day " << testDay
<< " invalid. Set to day 1.\n";

return 1;
}

class Employee {

public:
Employee(const char *, const char *,
const Date &, const Date & );

void print() const;
~Employee();

private:
char firstName[ 25 ];
char lastName[ 25 ];
const Date birthDate; //composition: member object
const Date hireDate; //composition: member object
};


Employee::Employee(const char *first, const char *last,
const Date &dateOfBirth, const Date &dateOfHire )
: birthDate( dateOfBirth ),
hireDate( dateOfHire )
{
int length = strlen( first );
length = ( length < 25 ? length : 24 );
strncpy( firstName, first, length );
firstName[ length ] = '\0';

length = strlen( last );
length = ( length < 25 ? length : 24 );
strncpy( lastName, last, length );
lastName[ length ] = '\0';

cout << "Employee object constructor: "
<< firstName << ' ' << lastName << endl;
}

void Employee::print() const
{
cout << lastName << ", " << firstName << "\nHired: ";
hireDate.print();
cout << " Birth date: ";
birthDate.print();
cout << endl;
}

Employee::~Employee()
{
cout << "Employee object destructor: "
<< lastName << ", " << firstName << endl;
}


int main()
{
Date birth( 7, 24, 1949 );
Date hire( 3, 12, 1988 );
Employee manager( "Bob", "Jones", birth, hire );

cout << '\n';
manager.print();

cout << "\nTest Date constructor "
<<"with invalid values:\n";
Date lastDayOff(14,35,1994); //invalid month and day
cout << endl;

return 0;
}
خروجی برنامه فوق به صورت زير می باشد:

Date object constructor for date 7/24/1949
Date object constructor for date 3/12/1988
Employee object constructor: Bob Jones

Jones, Bob
Hired: 3/12/1988 Birth date: 7/24/1949

Test Date constructor with invalid values:
Month 14 invalid. Set to month 1.
Day 35 invalid. Set to day 1.
Date object constructor for date 1/1/1994

Date object destructor for date 1/1/1994
Employee object destructor: Jones, Bob
Date object destructor for date 3/12/1988
Date object destructor for date 7/24/1949
Date object destructor for date 3/12/1988
Date object destructor for date 7/24/1949
در برنامه فوق دو کلاس Date و Employee ايجاد شده است. کلاس Employee دارای چهار عضو داده خصوصی به نامهای firstName و lastName و birthDate و hireDate می باشد. اعضای birthDate و hireDate خود اشياء ثابتی از نوع کلاس Date می باشند که دارای اعضای داده خصوصی month و day و year هستند. تعريف تابع سازنده کلاس Employee مشخص می کند که سازنده چهار آرگومان fname و lname و dateOfBirth و dateOfHire را دريافت می کند. دو آرگومان اول اعضای firstName و lastName را مقدار دهی می کنند. دو آرگومان بعدی يعنی dateOfBirth و dateOfHire توسط birthDate و hireDate که اشيايی از نوع کلاس Date می باشند به سازنده های اين اشياء ارسال می گردند و اين دو شیء را مقدار دهی اوليه می کنند.

Employee::Employee(const char *first, const char *last,
const Date &dateOfBirth, const Date &dateOfHire )
: birthDate( dateOfBirth ),
hireDate( dateOfHire )
همانطور که در دستور فوق می بينيد بعد از ليست آرگومانها علامت ( : ) قرار گرفته است و بعد از آن نام دو شیء عضو کلاس Employee که از نوع کلاس Date می باشند، قرار دارد. اين روش برای مقدار دهی اوليه به اشياء عضو يک کلاس از طريق آرگومانهای کلاس اصلی، مورد استفاده قرار می گيرد.

نکته:تابع عضو print از کلاس Date ، در تابع سازنده اين کلاس بدون هيچ آرگومانی فراخوانی شده است. اين کار در ++C بسيار مرسوم است. در اينجا در تابع print مشخص شده است که کدام يک از اعضای کلاس بايد چاپ شود، و نيازی به دريافت آرگومان ندارد.
 

!MAHSA!

کاربر ويژه
توابع دوست و کلاس های دوست

توابع دوست و کلاس های دوست


تابع دوست يک کلاس در خارج از حوزه آن کلاس تعريف می شود اما همچنان به اعضای غير عمومی آن کلاس دسترسی دارد. توابع تنها (توابعی که عضو کلاسی نمی باشند) و يا توابع عضو يک کلاس می توانند به عنوان دوست کلاس ديگری تعريف گردند. برای تعريف يک تابع به عنوان دوست يک کلاس، پيش تعريف تابع را در آن کلاس به همراه کلمه friend می آوريم.

class Count{
friend void setX(Count & , int);
int x;
}
در دستور فوق تابع setX به عنوان دوست کلاس Count در نظر گرفته می شود. برای اينکه تمام توابع عضو کلاسی با نام classTwo به عنوان توابع دوست کلاس ديگری با نام classOne در نظر گرفته شوند. دستور زير را در تعريف کلاس classOne می آوريم:

friend class classTwo;
در برنامه زير تابع setX به عنوان دوست کلاس Count در نظر گرفته شده است لذا اجازه دستيابی به عضو داده x از کلاس Count را دارا می باشد. ضمنا بهتر است که توابع دوست در ابتدای تعريف اعضای کلاس ، تعريف گردند.

#include <iostream.h>

class Count {
friend void setX( Count &, int );

public:
Count(): x( 0 ) // initialize x to 0
{
// empty body
}

void print() const
{
cout << x << endl;
}

private:
int x;
};

void setX( Count &c, int val )
{
c.x = val; // legal: setX is a friend of Count
}

void main()
{
Count counter;

cout << "counter.x after instantiation: ";
counter.print();

setX( counter, 8 ); // set x with a friend

cout << "counter.x after call"
<<"to setX friend function: ";
counter.print();
}
خروجی برنامه فوق به صورت زير می باشد:

counter.x after instantiation: 0
counter.x after call to setX friend function: 8
در برنامه فوق توابع عضو کلاس Count در داخل کلاس تعريف شده اند. همچنين عضو داده خصوصی x ، توسط دستور زير با عدد 0 مقدار دهی اوليه شده است.

Count(): x( 0 )
 

!MAHSA!

کاربر ويژه
اشاره گر this

اشاره گر this


در ++C کلمه کليدی this همراه با کلاس ها وجود دارد. هر شیء از طريق اشاره گری به نام this به آدرس خود دسترسی دارد. از اين اشاره گر می توان برای بررسی اينکه آيا آرگومان ارسال شده به تابع عضو يک شیء، خود شیء می باشد يا خير ، استفاده کرد. به برنامه زير توجه نماييد:

#include <iostream.h>

class CDummy {
public:
int isitme (CDummy& param);
};

int CDummy::isitme (CDummy& param)
{
if (&param == this) return 1;
else return 0;
}

int main () {
CDummy a;
CDummy* b = &a;
if ( b->isitme(a) )
cout << "yes, &a is b";
return 0;
}
خروجی برنامه فوق به صورت زير می باشد:

yes, &a is b
 

!MAHSA!

کاربر ويژه
مدیریت حافظه پویا

مدیریت حافظه پویا


++c برنامه نویسان را قادر می سازد تا تخصیص حافظه و یا آزادسازی حافظه را برای هر نوع داده ای در برنامه مدیریت و کنترل کنند. این قابلیت، مدیریت حافظه پویا نامیده می شود و توسط عملگرهای new و delete صورت می پذیرد. دو عملگر مذکور در فایل new.h قرار دارند. لذا برای به کارگیری این دو عملگر باید از دستور #include <new.h> در برنامه استفاده کنیم. دستورات زیر را در نظر بگیرید:

Time *timePtr;
timePtr = new Time;
عملگر new در دستورات فوق شیئی را با اندازه داده ای از نوع Time می سازد و سازنده این شیء را فراخوانی کرده و یک اشاره گر از نوع داده قرار گرفته در سمت راست عملگر new برمی گرداند. توجه داشته باشید که عملگر new برای هر نوع داده ای (مانند double و int و ...) و یا کلاسها می تواند به کار رود. اگر new نتواند فضای خالی برای شیء در حافظه بیابد، یک اشاره گر0 را باز می گرداند. برای نابودسازی شیئی که به صورت پویا برای آن حافظه تخصیص داده شده، از عملگر delete به صورت زیر استفاده می کنیم:

delete timePtr;
دستور فوق ابتدا نابودکننده شیء را که timePtr به آن اشاره می کند، فراخوانی کرده؛ سپس حافظه اختصاص داده شده به شیء را آزاد می سازد و حافظه برای تخصیص به شیء دیگری آماده می شود.

++c اجازه مقداردهی اولیه را همزمان با تخصیص حافظه پویا به یک شیء می دهد. به دستور زیر توجه کنید:

double *ptr=new double(3.14159);
دستور فوق حافظه تخصیص داده شده به داده ای از نوع double را با 3.14159 مقداردهی می کندو اشاره گر به آن را در Ptr قرار می دهد. روش مشابه دستور فوق می تواند برای آرگومانهای تابع سازنده یک شیء به کار رود. به دستور زیر توجه کنید:

Time *timePtr= new Time(12,0,0);
دستور فوق حافظه تخصیص داده شده به شیئی از نوع کلاس Time را با 12 ظهر مقداردهی کرده و اشاره گر با آن را در timePtr قرار می دهد.

عملگر new امکان تخصیص حافظه پویا برای آرایه ها را نیز فراهم می سازد. به عنوان مثال برای یک آرایه 10 عنصری از نوع عدد صحیح توسط دستور زیر حافظه پویا تخصیص می یابد:

int *gradesArray=new int[10];
در دستور فوق اشاره گر به اولین عنصر در حافظه پویای تخصیص یافته به آرایه 10 عنصری از نوع عدد صحیح در اشاره گر gradesArray قرار می گیرد.

برای آزادسازی حافظه تخصیص داده شده به آرایه ها از دستور زیر می توان استفاده کرد:

delete [] gradesArray;
نکته: توجه داشته باشید که برای آزادسازی خانه های حافظه تخصیص یافته به یک آرایه از [] delete استفاده نمایید و delete را به تنهایی به کار نبرید.
 

!MAHSA!

کاربر ويژه
اعضای ایستای کلاس

اعضای ایستای کلاس


همان طور که می دانید متغیری که در یک تابع از نوع static تعریف می شد، هنگام خروج از تابع از بین نمی رفت و مقدار خود را خفظ می کرد و با فراخوانی مجدد تابع قابل دسترسی بود. در کلاسها نیز می توان اعضا را به صورت ایستا (static) تعریف کرد. چنین عضوی که به صورت ایستا تعریف می شود، برای همه اشیایی که از نوع آن کلاس تعریف می شوند، به صورت مشترک قابل دسترسی است و هنگامی که مقدار این عضو در یکی از این اشیا تغییر می کند مقدار جدید در سایر اشیا از نوع آن کلاس نیز قابل استفاده می باشد. چنین عضوی ممکن است مانند متغیر عمومی به نظر آید، اما این عضو دارای حوزه کلاس می باشد و مانند متغیر عمومی دارای حوزه فایل نیست. اعضای ایستای یک کلاس می توانند از نوع public ویا private باشند. همچنین اعضای ایستا از طریق توابع عضو کلاس و یا توابع دوست یک کلاس قابل دسترسی می باشند. ضمنا به اعضای ایستایی که به صورت عمومی تعریف شده اند، می توان مستقیما از طریق عملگر :):) دسترسی یافت.

برای آشنایی با نحوه به کار گیری اعضای ایستای کلاس و نیز مدیریت حافظه پویا به برنامه زیر توجه کنید:

#include <iostream.h>
#include <new.h>
#include <string.h>

class Employee {
public:
Employee( const char *, const char * );
~Employee();
const char *getFirstName() const;
const char *getLastName() const;

// static member function
static int getCount();

private:
char *firstName;
char *lastName;

// static data member
static int count;
};

int Employee::count = 0;

int Employee::getCount()
{
return count;
}

// constructor dynamically allocates space for
// first and last name and uses strcpy to copy
// first and last names into the object
Employee::Employee(const char *first,const char *last)
{
firstName = new char[ strlen( first ) + 1 ];
strcpy( firstName, first );

lastName = new char[ strlen( last ) + 1 ];
strcpy( lastName, last );

++count; // increment static count of employees

cout << "Employee constructor for " << firstName
<< ' ' << lastName << " called." << endl;
}

// destructor deallocates dynamically allocated memory
Employee::~Employee()
{
cout << "~Employee() called for " << firstName
<< ' ' << lastName << endl;

delete [] firstName; // recapture memory
delete [] lastName; // recapture memory

--count; // decrement static count of employees
}

const char *Employee::getFirstName() const
{
return firstName;
}

const char *Employee::getLastName() const
{
return lastName;
}

int main()
{
cout <<"Number of employees before instantiation is "
<<Employee::getCount() << endl;

Employee *e1Ptr = new Employee( "Susan", "Baker" );
Employee *e2Ptr = new Employee( "Robert", "Jones" );

cout << "Number of employees after instantiation is "
<< e1Ptr->getCount();

cout << "\n\nEmployee 1: "
<< e1Ptr->getFirstName()
<< " " << e1Ptr->getLastName()
<< "\nEmployee 2: "
<< e2Ptr->getFirstName()
<< " " << e2Ptr->getLastName() << "\n\n";

delete e1Ptr; //recapture memory
e1Ptr = 0; //disconnect pointer from free-store space
delete e2Ptr; //recapture memory
e2Ptr = 0; //disconnect pointer from free-store space

cout << "Number of employees after deletion is "
<< Employee::getCount() << endl;

return 0;
}
خروجی برنامه فوق به صورت زير می باشد:

Number of employees before instantiation is 0
Employee constructor for Susan Baker called.
Employee constructor for Robert Jones called.
Number of employees after instantiation is 2

Employee 1: Susan Baker
Employee 2: Robert Jones

~Employee() called for Susan Baker
~Employee() called for Robert Jones
Number of employees after deletion is 0
در برنامه بالا عضو داده خصوصی count و تابع عضو عمومی getCount به صورت ایستا (static) در کلاس Employee تعریف شدند، و توسط دستور زیر:

int Employee::count = 0;
عضو داده خصوصی count از کلاس Employee مقداردهی اولیه شد. count تعداد اشیای ایجاد شده از کلاس Employee را می شمارد و در خود نگهداری می کند. هر بار که یک شی جدید از نوع کلاس Employee ایجاد می شود توسط ستور count++ در سازنده کلاس count ، Employee مقدارش یک واحد افزایش می یابد و هر بار که یک شی از نوع کلاس Employee نابود می شود، توسط دستور count-- در نابود کننده کلاس count ، Employee مقدارش یک واحد کاهش می یابد.

دستورات زیر دو شی از نوع Employee را توسط عملگر new ایجاد می کنند و اشاره گر به حافظه تخصیص یافتهبه این دو شی به ترتیب در e1Ptr و e2Ptr قرار می گیرد:

Employee *e1Ptr = new Employee( "Susan", "Baker" );
Employee *e2Ptr = new Employee( "Robert", "Jones" );
همچین دستورات زیر حافظه تخصیص یافته به دو شی از نوع کلاس Employee را آزاد می سازند:

delete e1Ptr; //recapture memory
e1Ptr = 0; //disconnect pointer from free-store space
delete e2Ptr; //recapture memory
e2Ptr = 0; //disconnect pointer from free-store space
توجه داشته باشید، هنگامی که هنوز شیئی از نوع کلاس Employee ساخته نشده است ولی اعضای getCount و count از این کلاس موجود می باشد و می توان به مقدار count از طریق تابع عضو getCount دسترسی پیدا کرد در برنامه فوق دستور زیر این کار را انجام می دهد:

Employee::getCount()
 

!MAHSA!

کاربر ويژه
مبانی گرانبار کردن عملگرها

مبانی گرانبار کردن عملگرها


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

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

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

#include <iostream.h>

class CVector
{
public:
CVector (int =0 ,int =0 );
CVector operator+ (CVector);
void showCVector(void);
private:
int x,y;
};

CVector::CVector (int a, int b)
{
x = a;
y = b;
}

CVector CVector::eek:perator+ (CVector vector)
{
CVector temp;
temp.x = x + vector.x;
temp.y = y + vector.y;
return temp;
}

void CVector::showCVector(void)
{
cout << "("<< x << "," << y << ")";
}

int main ()
{
CVector a (3,1);
CVector b (1,2);
CVector c;
c = a + b;
a.showCVector();
cout << "+";
b.showCVector();
cout << "=";
c.showCVector();
return 0;
}
خروجی برنامه به صورت زير می باشد :

(3,1)+(1,2)=(4,3)
 

!MAHSA!

کاربر ويژه
توابع عملگر به عنوان اعضای کلاس يا توابع دوست

توابع عملگر به عنوان اعضای کلاس يا توابع دوست


توابع عملگر می توانند توابع عضو يا توابع غير عضو باشند، که البته توابع غير عضو معمولا به صورت توابع دوست می باشند. هنگام فراخوانی يک تابع عملگر غير عضو ، عملوند ها بايد به عنوان آرگومان به تابع فرستاده شوند.

هنگام گرانبار کردن عملگر های () ، [] و <- يا هر عملگر انتساب ديگری، تابع گرانبار کننده عملگر بايد به عنوان يک تابع عضو تعريف شود ولی برای ساير عملگرها، تابع گرانبار کننده عملگر می تواند به صورت تابع غير عضو تعريف شود.

هنگامی که يک تابع عملگر به عنوان يک تابع عضو تعريف می شود، عملوند سمت چپ بايد شيئی از کلاس عملگر باشدو اگر لازم است که عملوند سمت چپ شيئی از کلاس ديگری باشد، اين تابع عملگر بايد از نوع تابع غير عضو تعريف شود. تابع عملگر غير عضو در صورتی که بايد به يک عضو خصوصی (private) کلاس دسترسی داشته باشد، بايد از نوع توابع دوست (friend) تعريف گردد.

تابع عملگر عضو کلاس تنها هنگامی فراخوانی می شود که عملوند سمت چپ عملگر دودويی (دو عملوندی) شيئی از نوع کلاس تابع عملگر باشد و يا تنها عملوند عملگر يگانی (تک عملوندی) شيئی از نوع کلاس عملگر باشد.

دليل ديگری که ممکن است يک تابع غير عضو برای گرانبار کردن يک عملگر به کار رود اينست که بخواهيم عملگر خاصيت جابجايی داشته باشد. به عنوان مثال فرض کنيد متغيری به نام number از نوع long int داريم و شيئی با نام bigInteger1 از نوع کلاس HugeInteger (کلاسی که در آن اعداد صحيح می توانند مقادير خيلی بزرگتری نسبت به مقاديرقابل ايجاد توسط انواعی مانند long int و double داشته باشند). عملگر جمع (+) هنگام جمع يک عدد HugeInteger با يک عدد long int (مثلا در عبارت bigInteger1+number) و يا جمع يک عدد long int با يک عدد HugeInteger (مثلا در عبارت number+bigInteger1) ، شيئی موقت از نوع کلاس HugeInteger ايجاد می کند، لذا اين عملگر بايد خاصيت جابجايی داشته باشد (که معمولا عملگر جمع در عبارات محاسباتی دارای خاصيت جابجايی می باشد). در اينجا اگر عملگر جمع به صورت تابع عضو تعريف گردد، در سمت چپ آن تنها بايد شيئی از کلاس HugeInteger قرار گيرد، لذا امکان ايجاد خاصيت جابجايی در اين حالت ميسر نمی باشد، به همين دليل اين عملگر را به صورت تابع غير عضو گرانبار می کنيم تا بتوانيم خاصطت جا بجايی را به آن بدهيم.

در زير برنامه ای آورده شده است که عمل جمع و تفريق را برای نقاط صفحه مختصات انجام می دهد. در اين برنامه عملگر تفريق به صورت تابع غير عضو دوست تعريف شده است و عملگر جمع به صورت تابع عضو تعريف شده است:

#include <iostream.h>

class CVector
{
friend CVector operator- (CVector & , CVector);

public:
CVector (int =0 ,int =0 );
CVector operator+ (CVector);
void showCVector(void);
private:
int x,y;
};

CVector::CVector (int a, int b)
{
x = a;
y = b;
}

void CVector::showCVector(void)
{
cout << "("<< x << "," << y << ")";
}

CVector CVector::eek:perator+ (CVector vector)
{
CVector temp;
temp.x = x + vector.x;
temp.y = y + vector.y;
return temp;
}

CVector operator- (CVector &v, CVector vector)
{
CVector temp;
temp.x = v.x - vector.x;
temp.y = v.y - vector.y;
return temp;
}
int main ()
{
CVector a (3,1);
CVector b (1,2);
CVector c,d;
c = a + b;
a.showCVector();
cout << "+";
b.showCVector();
cout << "=";
c.showCVector();

cout << "\n";

d = a - b;
a.showCVector();
cout << "-";
b.showCVector();
cout << "=";
d.showCVector();
return 0;
}
خروجی برنامه به صورت زير می باشد :

(3,1)+(1,2)=(4,3)
(3,1)-(1,2)=(2,-1)
 

!MAHSA!

کاربر ويژه
گرانبار کردن عملگر های >> و <<

گرانبار کردن عملگر های >> و <<


++C قادر به دريافت داده های تعريف شده در اين زبان و ارسال آنها به خروجی می باشد و برای اينکار از عملگر های >> و << استفاده می کند. اين عملگر ها برای انواع داده ای موجود در اين زبان گرانبار شده اند. ما نيز می توانيم اين عملگر ها را برای انواع داده و کلاسهايی که خودمان ايجاد کرده ايم، گرانبار کنيم. در برنامه زير کلاسی با نام phoneNumber که شماره تلفن را در خود نگهداری می کند ايجاد شده است. در اين برنامه توسط عملگر << يک شماره تلفن از ورودی دريافت می شود و توسط عملگر >> اين شماره در صفحه نمايش چاپ می گردد. به اين برنامه توجه کنيد:

#include <iostream.h>

class PhoneNumber {
friend ostream &operator<<
( ostream&, const PhoneNumber & );
friend istream &operator>>
( istream&, PhoneNumber & );

private:
char areaCode[ 4 ]; // 3-digit area code and null
char exchange[ 4 ]; // 3-digit exchange and null
char line[ 5 ]; // 4-digit line and null
};

ostream &operator<<
( ostream &output, const PhoneNumber &num )
{
output << num.areaCode << " "
<< num.exchange << " " << num.line;

return output; // enables cout << a << b << c;
}

istream &operator>>
( istream &input, PhoneNumber &num )
{
input >> num.areaCode; // input area code
input >> num.exchange; // input exchange
input >> num.line; // input line

return input; // enables cin >> a >> b >> c;
}

int main()
{
PhoneNumber phone;

cout << "Enter phone number in the form "
<< "123 456 7890:\n";

cin >> phone;

cout << "The phone number entered was: ";

cout << phone << endl;

return 0;
}
خروجی برنامه به صورت زير می باشد :

Enter phone number in the form 123 456 7890:
021 224 5348
The phone number entered was: 021 224 5348
همانطور که در برنامه فوق می بينيد توابع عملگرهای >> و << از نوع توابع دوست تعريف شده اند. چون عملگر >> دارای يک عملوند سمت چپ از نوع ostream & می باشد مانند cout در دستور cout<<classObject و نيز عملگر << دارای يک عملوند سمت چپ از نوعistream & می باشد مانند cin در دستور cin>>classObject. همچنين اين توابع گرانبار شده بايد به اعضای داده شيئی که بايد از ورودی دريافت و در خروجی چاپ شود، دسترسی داشته باشند. به دلايل ذکر شده اين توابع از نوع توابع عملگر دوست تعريف شده اند.

تابع عملگر operator>> يک آرگومان از نوع istream با نام input و آرگومان ديگری از نوع کلاس phoneNumber با نام num در يافت می کند و خروجی تابع از نوع istream می باشد. اين تابع شماره تلفن هايی به صورت

800 555 1212
را از ورودی دريافت کرده و آنها را در شيئی از نوع کلاس phoneNumber قرار می دهد. هنگامی که کامپايلر دستور زير را می بيند:

cin>>phone;
تابع operator>> به صورت زير توليد می شود:

operator>>(cin,phone);
هنگامی که تابع فوق توليد شد، آرگومان ارجاعی input نام مستعار cin و آرگومان ارجاعی num نام مستعاری برای phone در نظر گرفته می شوند. بدين ترتيب سه رشته دريافت شده از ورودی در اعضای areacode ، exchange و line قرار می گيرند.

تابع عملگر operator<< يک آرگومان از نوع ostream با نام output و آرگومان ديگری از نوع کلاس phoneNumber با نام num در يافت می کند و خروجی تابع از نوع ostream می باشد. اين تابع شماره تلفن هايی به صورت

800 555 1212
را که شيئی از نوع کلاس phoneNumber می باشد، نمايش می دهد. هنگامی که کامپايلر دستور زير را می بيند:

cout<<phone;
تابع operator<< به صورت زير توليد می شود:

operator<<(cout,phone);
هنگامی که تابع فوق توليد شد، آرگومان ارجاعی output نام مستعار cout و آرگومان ارجاعی num نام مستعاری برای phone در نظر گرفته می شوند. بدين ترتيب سه رشته موجود در اعضای areacode ، exchange و line به شيوه مورد نظر نمايش می يابند.
 

!MAHSA!

کاربر ويژه
گرانبار کردن عملگر های يگانی

گرانبار کردن عملگر های يگانی


به طور کلی برای گرانبار کردن يک عملگر يگانی به صورت تابع عضو ، به شيوه زير آن را تعريف می کنيم:

class نام کلاس{
public:
نوع خروجی عملگر oprerator علامت عملگر() const;
...
}
و برای گرانبار کردن يک عملگر يگانی به صورت تابع دوست ، به شيوه زير آن را تعريف می کنيم:

class نام کلاس{
friend نوع خروجی عملگر oprerator علامت عملگر
(const نام کلاس &);
...
}
 

!MAHSA!

کاربر ويژه
گرانبار کردن عملگر های دودویی

گرانبار کردن عملگر های دودویی


به طور کلی برای گرانبار کردن يک عملگر دودويی به صورت تابع عضو ، به شيوه زير آن را تعريف می کنيم:

class نام کلاس{
public:
const نام کلاس &oprerator علامت عملگر
(const نام کلاس &);
...
}
و برای گرانبار کردن يک عملگر دودويی به صورت تابع دوست ، به شيوه زير آن را تعريف می کنيم:

class نام کلاس{
friend const نام کلاس &oprerator علامت عملگر
( نام کلاس &, const نام کلاس &);
...
}
 

!MAHSA!

کاربر ويژه
تبديل انواع به يکديگر

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

عملگر تبديل نيز می تواند شيئی از کلاسی را به شيئی از کلاسی ديگر يا انواع اوليه تبديل نمايد. اين عملگر بايد به صورت تابع عضو تعريف شود. به دستور زير توجه نماييد:

test::eek:perator char* () const;
دستور فوق يک يک تابع تبديل را گرانبار می نمايد. اين تابع يک شیء موقت از نوع *char را به شيئی از نوع ايجاد شده توسط کاربر با نام test تبديل می نمايد. همانطور که مشاهده می کنيد برای اين تابع نوع خروجی در نظر گرفته نشده است. در حقيقت عملگر تبديل نبايد نوع خروجی داشته باشد و نوع خروجی ، نوع شيئی می باشد که در حال تبديل است. مثلا اگر s شيئی از کلاس test در دستور بالا باشد هنگامی که کامپايلر به عبارت (char *) s برخورد می کند، فراخوانی s.operator char *() ايجاد می گردد.

myClass::eek:perator int() const;
myClass::eek:perator otherClass() const;
دو دستور فوق به ترتيب عملگر تبديلی برای تبديل شيئی از کلاس myClass به يک عدد صحيح و دومی عملگر تبديلی برای تبديل شيئی از کلاس myClass به شيئی از کلاس ديگری با نام otherClass تعريف می کنند.
 

!MAHSA!

کاربر ويژه
گرانبار کردن عملگر های ++ و --

گرانبار کردن عملگر های ++ و --


در ++C می توان عمگر های ++ و -- را گرانبار کرد. به طور کلی برای گرانبار کردن اين عملگر ها به عنوان تابع عضو به صورت زير عمل می کنيم:


نوع داده operator++() //++x
{
...
}

نوع داده operator++(int x) //x++
{
...
}

نوع داده operator--() //--x
{
...
}

نوع داده operator--(int x) //x--
{
...
}
و برای گرانبار کردن عملگرهای فوق توسط توابع دوست ، به شيوه زير آنها را تعريف می کنيم:

friend نوع داده operator++(نوع داده &op) //++x
{
...
}

friend نوع داده operator++(نوع داده &op, int x) //x++
{
...
}

friend نوع داده operator--(نوع داده &op) //--x
{
...
}

friend نوع داده operator--(نوع داده &op, int x) //x--
{
...
}
 
بالا