همانطور که میدانید، شیوه معرفی اشیاء کلاسهای تعریف شده در ++C همانند متغیرهای عادی هستند. به عنوان مثال اگر کلاسی به نام myclass تعریف کرده باشیم، عبارت زیر یک شیء از این کلاس به نام a تعریف میکند:
اما اشیاء کلاس یک تفاوت اساسی با متغیرهای معمولی (مانند int ،float ،char و ...) دارند و آن عدم پشتیبانی از عملگرها است. در واقع عملگر انتساب (=) تنها عملگر قابل استفاده برای اشیاء کلاس است. اشیاء کلاس به صورت پیشفرض از عملگرهای دیگر (همانند + ، - ، / ، >> ، & و * و ...) پشتیبانی نمیکنند. اگر b ،a و c سه شیء از کلاس myclass باشند، عبارت زیر کامپایل نمیشود:
چرا که عمل جمع برای این اشیاء مفهومی ندارد. مگر اینکه خودمان زحمت مفهوم دادن به این عملگر را با استفاده از سربارگذاری عملگرها بکشیم. در واقع سربارگذاری به ما این امکان را میدهد که عملکرد هر عملگر را برای کلاس مشخص کنیم.
توجه داشته باشید که اگر کلاس ما حاوی دستوراتی برای تخصیص حافظه باشد، حتی عملگر انتساب پیشفرض هم قابل استفاده نیست. به قطعه کد زیر توجه کنید:
class myclass {
char *str;
public:
myclass(int n) {
str = new char[n];
}
~myclass() {
delete[] str;
}
.
.
.
};
اگر a و b دو شیء از این کلاس باشند، عبارت زیر یک خطای منطقی بسیار بزرگ را به دنبال دارد:
در وهله اول به نظر میآید که این عبارت مشکلی نداشته باشد. اما اینطور نیست. حین عمل انتساب، محتویات اشارهگر str مربوط به شیء b به همتای خودش در شیء a کپی میشود. این عمل باعث بروز دو مشکل میشود: اولا محتویات اشارهگر str شیء a (و در واقع آدرس حافظه پویای اختصاص پیدا کرده توسط شیء a) از دست میرود که متعاقب آن نمیتوان حافظه اختصاصی را آزاد کرد. ثانیا a و b از حافظه مشترک استفاده میکنند که به احتمال بسیار زیاد باعث ایجاد اختلال در برنامه خواهد شد.
همین مثال ساده نشان میدهد استفاده از سربارگذاری عملگرها از بروز چه مشکلاتی جلوگیری میکند. شما با تعریف مجدد عملگر انتساب برای این کلاس میتوانید از بروز خطا جلوگیری کنید.
فرض کنید کلاسی داریم به نام complex که برای کار با اعداد مختلط ایجاد شده است. این کلاس میتواند به صورت زیر تعریف شده باشد:
class complex {
private:
double r, i ;
public:
complex(int a = 0, int b = 0) {
r = a;
i = b;
}
};
این کلاس دو متغیر خصوصی دارد که برای ذخیره کردن قسمت حقیقی (r) و موهومی (i) عدد مختلط به کار میروند.
حال میخواهیم عملگر جمع را برای این کلاس تعریف کنیم.
complex operator + (const complex &b) {
complex temp;
temp.r = this->r + b.r;
temp.i = this->i + b.i;
return temp;
}
این تعریف ساده، عملگر + را برای جمع زدن دو عدد مختلط تعریف میکند. البته با این تعریف، کار سربارگذاری کامل عملگر + برای اعداد مختلط تمام نمیشود. ممکن است بخواهیم یک عدد حقیقی (یعنی متغیرهای معمولی ++C مانند int ،float و . . .) را با عدد مختلط جمع کنیم. تابع فوق تنها میتواند دو شیء از کلاس complex را با هم جمع کند و نه بیشتر.
این تابع یک ورودی دارد و یک خروجی. تکلیف خروجی تابع کاملا مشخص است. حاصل جمع دو عدد مختلط یک عدد مختلط است. پس تابع جمع، یک شیء از نوع complex را بر میگرداند. اما بر خلاف تصور اولیه ما، این تابع فقط یک ورودی دارد. در حالی که ما دو شیء را با هم جمع میکنیم. در واقع تابع جمع توسط شیء سمت چپ عملگر فراخوانی و شیء سمت راست به عنوان پارامتر به تابع ارسال میشود. به عنوان مثال فرض کنید که دو شیء c1 و c2 از کلاس complex در اختیار داریم. وقتی کامپایلر به عبارتی مانند c1 + c2 بر میخورد، آن را به صورت زیر تفسیر میکند:
به همین خاطر برای دسترسی به اعضای شیء سمت چپ (در اینجا c1) باید از اشارهگر ضمنی this استفاده کنیم. تابع جمع بالا به غیر از مواردی که بحث کردم مورد دیگری ندارد.
تابع فوق فقط برای جمع دو شیء از کلاس complex کاربرد دارد. حالت دیگری که ممکن است به وجود بیاد، جمع کردن یک متغیر معمولی ++C با اشیاء این کلاس است. همانطور که میدانید عمل جمع بین اعداد مختلط و حقیقی تعریف شده است. هر عدد حقیقی یک عدد مختلط با قسمت موهومی صفر است. این حالت خود به دو صورت اتفاق میافتد: عدد سمت چپ مختلط و عدد سمت راست حقیقی باشد، یا عدد سمت چپ حقیقی و عدد سمت راست مختلط باشد. حالت دوم نیاز به آشنایی با توابع دوست کلاس دارد. اما برای حالت اول میتوانیم از تابع زیر استفاده کنیم:
complex operator + (double x) {
complex temp;
temp.r = this->r + x ;
temp.i = this->i ;
return temp ;
}
تابع بالا دقیقا مانند تابع اولی تعریف شده است. با این تفاوت که پارامتر ورودی آن از شیء complex به متغیر از نوع double تغییر پیدا کرده است. به عنوان مثال عبارت c1 + 2.5 به این صورت تفسیر میشود:
به همین راحتی عملگر + برای اعداد مختلط تعریف شد. البته باید به نکات مهم زیر توجه کنید:
1- هنوز حالتی را که عملوند سمت چپ یک متغیر غیر مختلط باشد بررسی نکردهایم.
2- در تابع دوم لزومی ندارد که عدد ورودی حتما اعشاری باشد. ++C از تغییر موقت متغیر برای امکان استفاده از اعداد صحیح پشتیبانی میکند.
3- دو تابع مذکور بدون هیچ مشکلی میتوانند همزمان در یک کلاس تعریف شوند. ++C از سربارگذاری توابع (تعریف چند تابع با یک نام، اما با پارامترهای متفاوت) پشتیبانی میکند.
4 - تعاریف بالا تعریف عملگر جمع پیش فرض ++C را مخدوش نمیکند. یعنی برای جمع دو عدد صحیح یا اعشاری هیچ مشکلی ایجاد نمیکند.
5- علت استفاده از const و متغیر مرجع در پارامتر تابع اول مربوط به کاربرد متغیرهای مرجع است که قبلا در این مورد مطلبی نوشتهام.
6- در تعاریف بالا لزومی به استفاده از اشارهگر ضمنی this وجود ندارد. اما برای خوانایی بیشتر کد معمولا از این روش استفاده میشود. دو عبارت this->r و r با هم معادل هستند.
7- توابع سربارگذاری عملگرها حتما باید در قسمت public کلاس تعریف شوند.
+ |
- |
* |
/ |
% |
^ |
& |
| |
~ |
! |
= |
< |
> |
+= |
-= |
*= |
/= |
%= |
^= |
&= |
|= |
<< |
>> |
>>= |
<<= |
== |
!= |
<= |
>= |
&& |
|| |
++ |
-- |
->* |
, |
-> |
[] |
() |
new |
delete |
new [] |
delete [] |
عملگرهایی را که میتوان مجددا تعریف کرد