توضیح با استاده ما که طلبه ایم هوچی بلد نیستیم:46:نقل قول:
Printable View
توضیح با استاده ما که طلبه ایم هوچی بلد نیستیم:46:نقل قول:
کد رو که حتما مفصل مي ذارم . اين فقط براي آشنايي بود . براي اکثر عملگر ها کد مي ذارم حتما .
خيلي ممنون بابت مثال ها . ولي شما که دست ما رو ، رو کردين !! :31: من مي خواستم مثال هامو از کتاب ديتل بذارم که اونا رو هم شما زحمت کشيدين گذاشتين .نقل قول:
ولي باز هم ممنون . :20:
خوب ...
يه مثال مي زنم براي 10 عملگر . براي بقيه هم انشاءالله تو پست هاي بعدي .
براي عمگلرهاي :
يه مثال خيلي ساده . کلاس Rational . اعداد گويا . مي دونين که تو ++C چنين نوع داده اي وجود نداره . ما ايجاد مي کنيم و اين 10 عملگر رو براش سربارگذاري مي کنيم .کد:+ - * / += -= *= /= << >>
اين هدر تابعه :
در مورد سربارگذاري << و >> فعلا چيزي نمي گم . فقط يه کم نگاهشون کنين . تو پست هاي بعدي ان شاءالله . دو سه سطر نوشتم ديدم نه هنوز خيلي هم مونده . پاکشون کردم و به جاش اين جملات رو دارم مي نويسم. :46:کد:
#include <iostream>
using std::istream;
using std::ostream;
class Rational
{
friend ostream & operator << ( ostream &, const Rational & );
friend istream & operator >> ( istream &, Rational & );
public:
Rational ();
void simple ( int *, int * );
Rational operator + ( Rational );
Rational operator - ( Rational );
Rational operator * ( Rational );
Rational operator / ( Rational );
private:
int numerator, denominator;
};
اي کاش در مورد اين سوال بپرسين و من جواب بدم . واقعا نمي دونم چجوري بگم . ( کلا از اولش هم ارائه مطلب من ضعيف بوده . :41: )
با نگاه به هدر کلاس مي بينيم اين کلاسمون حاوي يه سازنده ، يه تابع عضو به نام simple که بعدا مي بينين چيکار مي کنه و چهار تابع عضو براي سربارگذاري عملگرهاي مربوطه . دو تا هم داده عضو داريم که اولي صورت کسر و دومي مخرج کسر رو ذخيره مي کنه .
اين هم Implementation کلاس :
سازنده مون به صورت مقدار 0 و به مخرج مقدار 1 ميده .کد:#include <iostream>
using std::endl;
#include "Rational.h"
Rational::Rational ()
{
numerator = 0;
denominator = 1;
}
void Rational::simple ( int *num, int *denom )
{
int i = 2;
while ( i <= 5 )
{
if ( ( ( *num % i ) == 0 ) && ( ( *denom % i ) == 0 ) )
{
*num /= i;
*denom /= i;
}
if ( ( ( *num % i ) != 0 ) || ( ( *denom % i ) != 0 ) )
i++;
}
}
Rational Rational::operator + ( Rational r )
{
Rational temp;
temp.denominator = denominator * r.denominator;
temp.numerator = r.denominator * numerator + denominator * r.numerator;
simple ( &temp.numerator, &temp.denominator );
return temp;
}
Rational Rational::operator - ( Rational r )
{
Rational temp;
temp.denominator = denominator * r.denominator;
temp.numerator = r.denominator * numerator - denominator * r.numerator;
simple ( &temp.numerator, &temp.denominator );
return temp;
}
Rational Rational::operator * ( Rational r )
{
Rational temp;
temp.numerator = numerator * r.numerator;
temp.denominator = denominator * r.denominator;
simple ( &temp.numerator, &temp.denominator );
return temp;
}
Rational Rational::operator / ( Rational r )
{
Rational temp;
if ( r.numerator != 0 )
{
temp.numerator = numerator * r.denominator;
temp.denominator = denominator * r.numerator;
simple ( &temp.numerator, &temp.denominator );
}
return temp;
}
ostream & operator << ( ostream & out, const Rational & r )
{
if ( r.numerator == 0 || r.denominator == 1 )
out << r.numerator << endl;
else
out << r.numerator << " / " << r.denominator << endl;
return out;
}
istream & operator >> ( istream & in, Rational & r )
{
in >> r.numerator;
in.ignore ( );
in >> r.denominator;
if ( r.denominator == 0 )
r.denominator = 1;
return in;
}
اين کلاس رو خودم اون زمان ها که استاد براي تمرين خواسته بود نوشتم . بنابراين تابع simple ام رو زياد جدي نگيرين . يه کم پياده سازيش اين جوريه :weird: . استاد مي گفت بايد يه تابع ديگه مي نوشتين براي کوچکترين مخرج و مشترک ، ولي من ننوشتم . کاملا گنگه . اين تابع به طور مثال عدد گوياي شش نهم رو مي کنه دو سوم . ربطي به سربارگذاري نداره . بي خيالش شين . :20:
حالا مي رسيم به سربارگذاري عملگر + . تابع سربارگذاري اين عملگر ، يه Rational مي گيره و با شيء this جمع مي کنه و يه Rational بر مي گردونه !!
عملگر + يه عملگر دودويي يا Binary هست . يعني دو عملوند ميگيره . يکيش تو سمت چپ و يکيش تو سمت راست . عملوند سمت راست رو به تابع از طريق آرگومان مي فرستيم ( r ) و عملوند سمت چپ هم به قول استادمون "خودش" هست . يعني this . يعني داده هاي عضو همون شيئي که اين تابع رو فراخواني کرده . در مثال بالا denominator همون this->denominator هست که ما از عملگر this به طور صريح استفاده نکرديم ولي Compiler به طور ضمني اون رو به کار مي گيره . نمي دونم تونستم 50 درصد مطلب رو برسونم يا نه ... :41:
يه شيء به اسم temp به طور موقت مي گيريم و نتيجه جمع رو تو اون ذخيره مي کنيم و temp رو برميگردونيم .
البته قبلش صورت و مخرج رو براي ساده سازي به تابع simple مي فرستيم .
سربارگذاري - و * و / هم به همين صورته .
حالا بعد از سربارگذاري عملگرهاي + و - و * و / ، سربارگذاري =+ و =- و =* و =/ به يه خط کد نياز دارن .
من اينا رو به هدر کلاس اضافه مي کنم و همون جا هم پياده سازي مي کنم :
کد:#include <iostream>
using std::istream;
using std::ostream;
class Rational
{
friend ostream & operator << ( ostream &, const Rational & );
friend istream & operator >> ( istream &, Rational & );
public:
Rational ();
void simple ( int *, int * );
Rational operator + ( Rational );
Rational operator - ( Rational );
Rational operator * ( Rational );
Rational operator / ( Rational );
Rational operator+= ( Rational r )
{
return *this = *this + r;
}
Rational operator-= ( Rational r )
{
return *this = *this - r;
}
Rational operator*= ( Rational r )
{
return *this = *this * r;
}
Rational operator/= ( Rational r )
{
return *this = *this / r;
}
private:
int numerator, denominator;
};
اين داستان همچنان ادامه دارد .......................
خوب حالا در مورد سربارگذاري عملگرهاي ورودي و خروجي يا همون << و >> :
براي اين قسمت سوال خيليه :
1- چرا خارج از کلاس تعريف شدن ؟ ( به صورت تابع Global يا سراسري )
2- چرا friend تعريف شدن ؟
3- چرا دو تا پارامتر مي گيرن ؟ ( بر خلاف ساير عملگرهاي دودويي که يه پارامتر مي گرفتن . )
4- چرا هم يه stream مي گيرن و هم يه stream برميگردونن ؟
5- چرا آدرس مي گيرن و برميگردونن ؟ ( & )
6- چرا براي ورودي ، شيئمون رو const تعريف نکرديم ولي براي خروجي const تعريف کرديم ؟
من به اين 6 سوال جواب ميدم . اگه باز هم سوال موند بگين تا بگم !
1- عملگر + به صورت تابع عضو تعريف شد . چون هر دو عملوند اون يه شيء از خود کلاس هستن . ولي در مورد عملگرهاي ورودي و خروجي ، يکي از اونا ( هميشه عملوند سمت چپ ) يه شيء از کلاس istream يا ostream هستن نه Rational . هميشه چنين عملگرهايي به صورت تابع سراسري تعريف ميشن .
2- براي اينکه بتونن به داده هاي عضو private کلاس دسترسي داشته باشن .
3- چون در مورد عملگري مثل + ، اين يک شيء از خود کلاس ( Rational ) است که تابع سربارگذاري شده رو احضارمي کنه . ولي در اينجا شيء cout يا cin اين کار رو انجام خواهد داد . پس از this اينجا خبري نيست و ما مجبوريم تنها شيء موجود رو براش بفرستيم .
4 و 5 - يه stream مي گيرن تا شيء cout يا cin تو اون قرار بگيره . و يه stream به صورت ارجاع ( & ) بر مي گردونن تا بشه از اين دو عملگر به صورت دنباله اي استفاده کرد . به شکل زير :
در اين حالت نوع برگشتي cout << r1 ، يک cout خواهد بود و دوباره مي تواند به صورت cout << r2 ارزيابي شود و يه cout برگردانده شود تا در ادامه دستور رو به شکل زير داشته باشيم : cout << r3کد:cout << r1 << r2 << r3;
حالا چرا آدرس مي گيرن ؟ :31:
اينو من بايد تو مبحث توابع مي گفتم . ولي ديدم همه دوست دارن بحث شيء گرايي شروع بشه و اين مطالب ديگه خسته کننده هستن . من هم نگفتم . حالا مجبورم اينجا بگم .
سه روش براي انتقال آرگومان به توابع وجود داره .
1- از طريق مقدار
2- از طريق ارجاع
3- از طريق ارجاع با اشاره گرها
1- همون روشي بود که تا حالا استفاده مي کرديم . يعني وقتي مي خواستيم چيزي رو به تابع پاس بديم يه کپي از اون ايجاد ميشد و به تابع فرستاده ميشد . در اين حالت اين تابع هر بلايي سر اين چيز ميوورد تو تابع فراخواننده تاثير نداشت . چون اون کپي اورجينالشو داره .
2- در اين حالت به جاي اينکه کپي چيز فرستاده بشه ، آدرس اون فرستاده ميشد . در اين حالت کپي نميشه . همون روشي که الان استفاده کرديم . در اين حالت ديگه هر گونه تغيير تو هر دو تابع دخالت داده ميشه . چون فقط آدرس کپي شده و فرستاده شده نه خود اون . يه مزيت هم که داره اينه که سرعت اجراي برنامه در اين حالت بالاست . چون ديگه قرار نيست چيزي کپي بشه .
حالت سوم هم مثل اوليه و فقط Syntax فرق مي کنه . در اين حالت پارامترهاي تابع يه اشاره گر هستن و ما آدرس رو به تابع مي فرستيم .
6- چون عملگر خروجي شيء رو تغيير نمي ده بنابراين بهتره const تعريف بشه ولي عملگر ورودي قراره شي رو تغيير بده . بنابراين نمي تونيم از const استفاده کنيم .
حالا در مورد خود سازوکار .
ما تو تابع main يه شيء به اسم r از کلاس Rational تعريف مي کنيم . بعد مي گيم cout << r . در اين حالت ، cout ميره به جاي out در پارامترهاي تابع ميشينه و r هم تو r . بنابراين دستورهاي خروجي رو با دستور out به جاي cout انجام ميديم و آخرسر اين out رو برميگردونيم .
باز هم ادامه دارد ...
با سلام
استاد آيا لطف خواهي كرد و pdf اين دروس را تهيه و لينك كني؟
قبلا" از لطف شما نهايت امتنان را دارم.
درود ...نقل قول:
ان شاءالله اگر عمري باقي بود و توفيقي از جانب پروردگار جهت ادامه حيات در اين دايره هستي نصيب اين جانب گرديد و بحث را توانست تا يه سمت و سويي کشاند و بحث شيء گرايي به اتمام رسيد ، اين PDF تنظيم ، و جهت دانلود شما بزرگواران لينک خواهد شد .
قبلا از حسن توجه حضرت عالي نهايت سپاس را دارم و از خداوند طول عمر و سلامت کامل را براي شما يار بزرگوار مسئلت دارم .
:20:
سلام ...
نکته مهم ديگه اي که تو سربارگذاري هست ، سربارگذاري عملگر هاي ++ پيشوندي و پسوندي است . در اين مورد فکر کنم قبلا بحث شده که اين دو چه فرقي با هم دارن .
يه مثال خيلي خيلي ساده مي زنم . اين تعريف و پياده سازي کلاسمون :
عملگر خروجي رو براي اين سربارگذاري کردم تا بتونم تو تابع main فرق دو عملگر پيشوندي و پسوندي يا Prefix و postfix رو نشون بدم . يه سازنده داريم که به تنها داده عضو ما که از نوع int هست مقدار صفر ميده . بعد پياده سازي تابع سربارگذاري ++ پيشوندي رو مي بينين . اين تابع يه ارجاع به کلاسمون برميگردنه و پارامتري نمي گيره . داخل تابع ، اول داده عضو مورد نظر خودمون رو يدونه ++ مي کنيم و بعد خود شيء رو به عنوان نتيجه برميگردونيم . حالا چجوري تابع سربارگذاري ++ پسوندي رو با اين متمايز کنيم ؟ کافيه در ليست پارامترها يه کلمه int بنويسيم . اين کلمه هيچ ارزشي نداره . مي بينين که يه متغير هم تعريف نکرديم و فقط int خالي نوشتيم . اين فقط براي اينه که اين دو تابع ++ از هم متمايز باشن . فقط نکته اي که تو سربارگذاري ++ پسوندي هست اينه که اول شيء خودمون رو تو يه شيء از نوع همون کلاس ذخيره مي کنيم . بعد خود شيء رو پلاس پلاس مي کنيم و بعد شيء پلاس پلاس نشده رو برمي گردونيم . فهميدين که چي شد ؟ :46:کد:#include <iostream>
using std::ostream;
class MyInt
{
friend ostream &operator<< ( ostream &, const MyInt & );
public:
MyInt () //Constructor
{
num = 0;
}
MyInt &operator++ () //Prefix increment
{
num++;
return *this;
}
MyInt operator++ ( int ) //postfix increment
{
MyInt a = *this;
num++;
return a;
}
private:
int num;
};
ostream & operator<< ( ostream &out, const MyInt &m )
{
out << m.num;
return out;
}
اين هم تابع main براي تست نتيجه کار :
يه شيء به نام s از کلاسمون مي گيريم . بعد ++s رو چاپ مي کنيم . اول مي بينين که s چاپ ميشه ( يعني 0 ) و بعد s يدونه ++ ميشه . يعني ميشه 1 . بعد s++ رو چاپ مي کنيم . اول s يدونه ++ ميشه يعني ميشه 2 و بعد چاپ ميشه .کد:#include <iostream>
using std::cout;
using std::endl;
#include "MyInt.h"
int main ()
{
MyInt s;
cout << s++;
cout << endl << ++s << endl;
system ("pause");
return 0;
}
تقريبا سربارگذاري رو اينجا تموم مي کنم .
یه چیزی بگم ناراحت نمیشی؟!
1 کم کثیف کاری! :دی
یه دفعه تمام Operator ها رو ریختی وسط، یکی رو گفتی، میگی بقیه هم همچنین!
شما بگو تئوریش اینجوریه.
بعد این ها Operator های ممکن اینها هستند.
اصولاً باید اینجوری تعریفشون کرد.
این هم 1 مثال مربوط به این یه تیکه.
و ...
نه که یه پست بدی برای تئوری، بقیه اش هم همینجوری.
البته فکر می کنم عجله من کار شما رو خراب کرد!
+
شما هنوز نگفتی این istream و ostream چی هستند، ازشون تو مبحث Operator ها فراوون استفاده می کنی!
همین دیگه!
اگر همین Operator ها رو یه بار درست بگی، می بینی چه شاگرد خوب و بگیری هستم! :گل:
مرسی
آقا خيلي ممنون . واقعا شرمنده کردين !! :20:نقل قول:
واقعا مطالب گنگ هستن ؟ تئوري رو که اول گفتم . با نگاه به مثال ها هم تقريبا ميشه فهميد قضيه چيه .
ولي خوب چون شما مي گين ، قبل از اينکه ارث بري رو شروع کنم يه بار ديگه در مورد اصول سربارگذاري مطالبي ميگم .
آخه يه کم عجله دارم . دو سه روز ديگه ميرم تا يه ماه بعد . مي خوام قبل از اينکه برم ، شيء گرايي رو تموم کنم .