تماس درباره   صفحه اصلی
  زبان ++C > شی گرائی و کلاس  
 
 

شی گرائی و کلاس


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

مفاهيم شی گرائی
کلاس
سازنده ها
مخرب ها
آرايه ای از اشيا


مفاهيم شی گرائی

برنامه نویسی شیءگرائی (object oreinted programming) وسیله ای برای مدل کردن صحیح دنیای واقعی با استفاده از اشیا (objects) در برنامه و استفاده مجدد از کد است. یک شی در برنامه دقیقا همان طور تعريف می شود که در دنيای واقعی است؛ خواص معینی دارد که آن را توصیف می کند و متدهایی که می توانید برای انجام کار معینی روی شیء استفاده کنید.

هدف کلی C++ اضافه کردن شیءگرائی به زبان برنامه نویسی C است. یک شیء برای نگهداری داده استفاده می شود. داده و توابعی که روی داده کار می کنند به هم مربوط هستند بنابراين داده و توابع هردو با هم دریک بسته قرار می گیرند. شیءگرائی بيشتر روی داده تاکيد دارد تا عمليات و توابعی که روی داده کار می کنند.

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

شرحی از داده ها و توابعی که می توانند روی داده کار کنند را کلاس (class) می نامند. کلاس را به عنوان الگوئی برای توليد شیء می توان تصور کرد. کلاس در واقع يک نوع داده user-defined است . اشياء نمونه هائی از کلاس ها هستند که در زمان اجرا ايجاد می شوند.

چهار مفهوم اصلی وجود دارند که اساس برنامه نویسی شیءگرائی را می سازند و توسط کلاس ها ارائه می شوند. این مفاهیم انتزاع (abstraction)، کپسوله کردن (encapsulation)، توارث (inheritance) و چندریختی (polymorphism) هستند.

انتزاع

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


مثال. درباره خصوصیات کلی وسيله نقليه بدون سروکارداشتن با یک وسيله و مدل خاص می توان بحث کرد. يک نمونه شیء MyVehicle از کلاس Vehicle که خواص کلی و توابع وسايل نقليه را در بر دارد می توان ايجاد کرد. تابع Print مشخصات کلی وسيله نقليه را نمايش می دهد.

Vehicle MyVehicle;
MyVehicle.Print();


کپسوله کردن

قرار دادن داده و توابعی که روی داده کار می کنند را در یک بسته کپسوله کردن می گویند. در برنامه نویسی رویه گرا (مشابه آنچه تا کنون انجام می داديد) معلوم نیست چه تابعی روی چه متغیری کار می کند. در برنامه های پیچیده تر این روابط تیره تر می شوند. در برنامه نویسی شیءگرائی داده و توابع مربوط به آن- که اغلب متد (method) ناميده می شوند- با در يک بسته به نام کلاس قرار می گیرند بنابراین کاملا مشخص است چه تابعی روی چه داده ای کار می کند.

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

وراثت

یکی ديگر از جنبه های مفید برنامه نویسی شیءگرائی قابلیت استفاده مجدد از کد است. یک کلاس می تواند اعضای عمومی را از کلاس دیگر را به ارث ببرد. توارث اجازه می دهد کلاس جديدی شامل کليه داده ها و توابع کلاس (های) پياده سازی شود. کلاس موجود را کلاس پايه (base) و کلاس جديد که اعضای کلاس پايه را به ارث می گيرد را کلاس مشتق شده (derived) می نامند.


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

سلسله مراتب کلاس

اگر کلاس ويژگی های تنها يک کلاس را به ارث ببرد وارثت منفرد(single inheritance) و اگر از چند کلاس به ارث گرفته شود وارثت چندگانه (multiple inheritance) ناميده می شود.

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

چندریختی

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

object.function(parameter-list)


مثال. در مثال قبل تابع نمايش برای قايق ممکن است متفاوت از نمايش يک وسيله نقليه باشد. دو تابع می توانند هم نام باشند ولی با کدهای متفاوت بسته به کلاس شیئی که تابع را فراخوانی می کند.

Vehicle MyVehicle;
Boat YourBoat;
MyVehicle.Print();
YourBoat.Print();


در مثال قبل کامپايلر نوع تابع print را برای دو نوع فراخوانی شده بر اساس نوع شیء می تواند پيدا کند. اما گاهی پيدا کردن آن تا زمان اجرای واقعی برنامه ممکن نيست. مشکل زمانی بروز می کند که به شیء از طريق اشاره گر دسترسی می شود و چون اشاره گرها می توانند بطور پويا به انواع مختلفی از اشيا اشاره کنند کلاس شیء تا زمان اجرا مشخص نمی شود. برای حل اين موضوع توابع عضو مجازی بکار می روند. توابع مجازی عضو (Virtual member functions) اجازه پياده سازی خاص تری از تابعی که فراخوانی می شود را مطابق با نوع شیء زمان اجرا می دهند.


کلاس

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

کلاس مشابه ساختمان تعریف می شود فقط کافی است ابتدای آن کلمه کلیدی class ذکر شود. دقت کنید در انتهای بلاک علامت سمیکولن را فراموش نکنید.

class classname
{
// members
};

وقتی یک کلاس ایجاد می کنید مانند هر متغیر دیگر می توانید یک نمونه از آن را بگیرید:

classname object_variable;


مثال.

class myclass {
   int number;
   void greeting();
};


حوزه اعضای کلاس

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

حوزه های دسترسی (access modifiers) توسط کلمات زیر مشخص می شوند:

• private. اعضايی که به صورت خصوصی مشخص می شوند تنها درون کلاس توسط توابع عضو و توابع دوست قابل دسترسی هستند و نمی توانند خارج از کلاس دسترسی شود.
• public. اعضای عمومی از هر تابعی حتی خارج از کلاس قابل دستیابی هستند.
• protected. اعضای محافظت شده نمی توانند بیرون از کلاس دسترسی شوند اما توسط توابع عضو خودش و توابع دوست و از کلاسی که از آن مشتق شده قابل دسترسی هستند.

نکته. در یک ساختمان همه اعضا عمومی هستند مگر اینکه آنرا به عنوان خصوصی یا محافظت شده تعریف کنید.


مثال. متغير number عضو خصوصی و تابع greeting خضو عمومی کلاس زير هستند.

class myclass {
   int number;
public:    void greeting();
};


نکته. در یک ساختمان همه اعضا عمومی هستند مگر اینکه آنرا به عنوان خصوصی یا محافظت شده تعریف کنید.


مثال. در ساختمان A توابع عضو به صورت عمومی تعريف شده اند و فيلدها به صورت خصوصی هستند.

struct A {
private:
   int i, j, k;
public:
   int f() {return i + j + k;}
   void g() {i = j = k = 0;}
};


توابع عضو

توابع عضو (member functions) توابعی هستند که درون کلاس تعريف می شوند و متعلق به کلاس هستند. توابع عضو می توانند به کلیه اعضای کلاس (اعم از عمومی، خصوصی و محافظت شده) بدون هیچ محودیتی دسترسی پیدا کنند.


مثال. تابع greeting تابع عضو کلاس است که به متغير خصوصی عضو number دسترسی دارد.

class myclass {
   int number;
public:
   void greeting() {
      for(int i = 0; i<= number;i++) cout << "Hello World \n";
   }
};


در مثال فوق کد تابع عضو به صورت درونی (inline) در کلاس قرار داده شده است. توابع درونی درنقطه فراخوانی به صورت خطی گسترش پيدا می کند به جای اينکه واقعا فراخوانی شود. توابع درونی درصورتی که بدنه تابع کوچک باشد روش کارآمدتری هستند. راه ديگر تعريف تابع عضو اين است که بدنه تابع بعد از بلاک کلاس قرار گيرد. سپس برای ارتباط تابع عضو با کلاس قبل از نام تابع نام کلاس بدنبال علامت (::) بايد ذکر شود. :: عملگر حوزه (scope operation) نام دارد و بيان کننده اين است که تابع متعلق به کلاس است.


مثال. کلاس فوق را به صورت زير نيز می توان نوشت.

class myclass {
   int number;
public:
   void greeting();
};
void myclass::greeting(){
   for(int i = 0; i<= number;i++)
      cout << "Hello World \n";
}


توابع دوست

توابع دوست (friend function) توابعی هستند که عضو کلاس نيستند اما به اعضای خصوصی کلاس دسترسی دارند. برای ايجاد يک تابع دوست در کلاس پروتوتايپ تابع را در بخش عمومی کلاس قرار داده و قبل از آن کلمه friend استفاده کنيد.


مثال. دربرنامه زير پيغام سه بار نمايش داده می شود. تابع set يک تابع دوست است که برای مقداردهی متغير خصوصی عضو کلاس استفاده شده است.

#include <iostream>
class myclass {
   int number;
public:
   void greeting();
   friend void set(myclass, int);
};
void myclass::greeting(){
      for(int i = 0; i<= number;i++)
         cout << "Hello World \n";
}
void set(myclass n, int value){
   n.number=value;
}
int main () {
   myclass myobject;
   set(myobject, 3);
   myobject.greeting();
   return 0;
}


نکته. يک تابع ممکن است دوست بيش از يک کلاس باشد.


سازنده ها

معمولا بعضی از اعضای کلاس قبل از استفاده نیاز به مقداردهی دارند. اين عمل توسط سازنده (constractor) انجام می گیرد که به شیء اين امکان را می دهد که هنگام ايجاد مقداردهی شود. سازنده تابعی است هم اسم کلاس که وقتی یک نمونه از کلاس گرفته می شود اتوماتیک فراخوانی می شود.

تابع سازنده می تواند دارای پارامتر باشد بنابراين زمان ايجاد شیء می توان به متغيرهای عضو مقادير اوليه داد. برای ارسال آرگومان به تابع سازنده بايد هنگام تعريف شیء مقدار آرگومان بعد از نام شیء درون پرانتز قرار گيرد.

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

برای تابع سازنده مقدار برگشتی ذکر نمی شود (حتی viod).


مخرب ها

تابع مخرب کلاس (destructor) کم و بیش عکس سازنده عمل می کند. یک مخرب وقتی فراخوانی می شود که یک شی از بین می رود. یک مخرب مشابه سازنده ساخته می شود فقط قبل از اسم آن علامت مد (~)قرار می گیرد. تابع مخرب اتوماتیک وقتی متغیر شیء از حوزه دسترسی خارج می شود (برای متغیرهای سراسری وقتی از تابع اصلی خارج می شود و برای متغیر محلی هنگام خروج از بلاک تابع) فراخوانی می شود.

مشابه سازنده ها تابع مخرب نيز نوع برگشتی ندارد.


مثال. توابع myclass در کلاس زير سازنده هستند. تابع ~myclass يک مخرب است که در انتهای تابع اصلی فراخوانی می شود و فايل متن را می بندد.

#include <fstream.h>
#include <iostream.h>
#include <string.h>
class myclass {
private:
   char msg[20];
   int loopcounter;
   fstream myfile;
public:
   void greeting();
   myclass();      // Constructor
   myclass(char greeting[20]); // Constructor
   ~myclass()    // Destructor
};
myclass::myclass(){
   myfile.open("input.txt",ios::in);
   myfile.getline(msg,20);
}
myclass::myclass(char greeting[20]) {
   strcpy(msg,greeting);
}
myclass::~myclass(){
   myfile.close();
}
void myclass::greeting(){
   cout << msg << "\n";
}
int main (){
   myclass myobject;
   myobject.greeting();
   return 0;
}

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

myclass myobject("Howdy from Texas!");


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


آرايه ای از اشيا

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


مثال. myarray آرايه ای از اشيا است که در برنامه اصلی با چهار عدد مقداردهی شده و نمايش داده می شود.

#include <iostream.h>
class display {
   int number;
public:
   display(int n) {this->number=n;}
   int show() { cout << this->number << endl; }
};
int main() {
   display myarray[4] = {1,2,3,4,};
   for (int i = 0; i < 4 ;i++ )
      myarray[i].show();
   return 0;
}


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


 


 


صفحه اصلی| PDF| درباره| تماس