PDA

View Full Version : مشکل در تعريف تابع در کلاس



adameh_bahal
دوشنبه 25 خرداد 1388, 21:18 عصر
من يکم تو تعريف تابع در کلاس مشکل دارم
مثلاً اينجا متوجه نميشم تابع جمع با چه فرمتی تعريف شده
کلاً هم تو تعريف تابع هم داخل کلاس هم بيرون کلاس مشکل دارم و نميدونم از چه فرمتی بايد استفاده کرد
ميخواستم اگه ميشه راهنمايی کنيد
اگه يه منبع که ساده توضيح داده باشه هم معرفی کنيد ممنون ميشم



class matris
{
public:
double** data;
int rowsize,colsize;
public:matris(int row,int col)
{
data=new double[row][col];
}
public:matris add(matris m1)
{
if((rowsize!=m1.rowsize) || (colsize!=m1.colsize)
cout<<"error";
else
{
matris m3(rowsize,colsize);
for (int i=0;i<rowsize;i++)
for (int j=0;j<rowsize;j++)
{
m3.data[i][j]=data[i][j]+m1.data[i][j];
}
return m3;
}
}

}

main()
{
matris m1(3,4);
}

PC2st
دوشنبه 25 خرداد 1388, 22:16 عصر
public:matris(int row,int col)
{
data=new double[row][col];
}
تابع بالا، همون تابع سازنده کلاس هست به نام matris که نوع خروجی اش رو نباید مشخص کرد، که میشد به شکل زیر هم نوشتش:

public:
matris(int row,int col)
{
data=new double[row][col];
}


public:matris add(matris m1)
{
if((rowsize!=m1.rowsize) || (colsize!=m1.colsize)
cout<<"error";
else
{
matris m3(rowsize,colsize);
for (int i=0;i<rowsize;i++)
for (int j=0;j<rowsize;j++)
{
m3.data[i][j]=data[i][j]+m1.data[i][j];
}
return m3;
}
}
تابع بالا به نام add هست که خروجی اش از نوع matris می‌باشد که بصورت public هم هست و به شکل زیر میشد نوشتش:

public:
matris add(matris m1)
{
if((rowsize!=m1.rowsize) || (colsize!=m1.colsize)
cout<<"error";
else
{
matris m3(rowsize,colsize);
for (int i=0;i<rowsize;i++)
for (int j=0;j<rowsize;j++)
{
m3.data[i][j]=data[i][j]+m1.data[i][j];
}
return m3;
}
}


main()
{
matris m1(3,4);
}این هم که تابع main هست و نقطه آغازین اجرای برنامه، ولی در اینجا نوع تابع مشخص نشده، تا جایی که اطلاع دارم، تو استاندارد c++98 باید نوع خروجی توابع مشخص بشه. در اینجا چون نوع خروجی مشخص نشده، خودش اون رو int در نظر میگیره. تو بدنه تابع main هم باید یک مقدار return بشه، چون در اینجا مقداری برگشت داده نشده، کامپایلر واسش مقدار صفر رو در نظر می‌گیره.

adameh_bahal
دوشنبه 25 خرداد 1388, 22:22 عصر
[/code
public:matris add(matris m1)
{
if((rowsize!=m1.rowsize) || (colsize!=m1.colsize)
cout<<"error";
else
{
matris m3(rowsize,colsize);
for (int i=0;i<rowsize;i++)
for (int j=0;j<rowsize;j++)
{
m3.data[i][j]=data[i][j]+m1.data[i][j];
}
return m3;
}
}
[/code]تابع بالا به نام add هست که خروجی اش از نوع matris می‌باشد .


اين قسمت رو ميشه بيشتر توضيح بديد

PC2st
دوشنبه 25 خرداد 1388, 22:36 عصر
کدوم قسمتش رو توضیح بدهم؟

adameh_bahal
دوشنبه 25 خرداد 1388, 23:22 عصر
کدوم قسمتش رو توضیح بدهم؟


مثلاً اينکه خروجيش از چه نوعي هست رو از کجا تشخيص داديد؟
يا اينکه چرا مثلاً اينجوری نوشته نشده:

int add ( int m1)

بيشتر اين قسمت رو متوجه نميشم:

matris add(matris m1)

PC2st
دوشنبه 25 خرداد 1388, 23:42 عصر
نوع خروجی که سمت چپ نوشته شده، نام تابع بعد از نوع خروجی نوشته میشه و سپس نوع و نام آرگومان توابع در داخل پرانتز نوشته میشه:


return_type function_name (arg1_type arg1_name)
return_type همون نوع خروجی
function_name نام تابع
arg1_type نوع آرگومان اول (نوع پارامتر اول)
arg1_name نام آرگومان اول (نام پارامتر اول)


مثلاً اينکه خروجيش از چه نوعي هست رو از کجا تشخيص داديد؟از اینکه تو تعریف تابع اینطور نوشته:

matris add(matris m1)پس نوع خروجی تابع از نوع matris هست. نوع matris هم که همون نوع خود کلاس است پس تابع add یک نمونه شیئ از خود کلاس رو بر میگردونه، و این تابع یک آرگومان بنام m1 داره که از نوع matris هست. پس تابع add یک نمونه شیئ از نوع matris رو بعنوان پارامتر میگیره و سپس یک خروجی میده که اونهم یک نمونه شیي از نوع matris هست.


يا اينکه چرا مثلاً اينجوری نوشته نشده:چرا اینجور نوشته بشه؟ اگه اینطور که شما نوشتی میخواست نوشته بشه، اونوقت دیگه کاربرد تابع add فرق میکرد، در اینصورت یک عدد میگرفت و سپس یک عدد هم پس میداد (خروجی از نوع عدد میداد) و این اون کاربردی نیست که مدر نظر بوده، ما میخواستیم یک تابع بنام add داشته باشیم، که یک متغیر از نوع matris بگیره و بعد از پردازش روی اطلاعات متغیر و انجام محاسبات، یک متغیر جدید تولید میکنه و بعنوان خروجی تابع add به ما برمیگردونه. پس به همین خاطر این تابع اینطور تعریف شده. نمیدونم تا چه حد پاسخ من مرتبط بود.

پی نوشت: cpp زبان انعطاف پذیری هست، میشد تعریف تابع رو اینطور هم نوشت:


matris
add
(matris m1) {
....}

adameh_bahal
سه شنبه 26 خرداد 1388, 00:04 صبح
از اینکه تو تعریف تابع اینطور نوشته:

matris add(matris m1)پس نوع خروجی تابع از نوع matris هست. نوع matris هم که همون نوع خود کلاس است پس تابع add یک نمونه شیئ از خود کلاس رو بر میگردونه، و این تابع یک آرگومان بنام m1 داره که از نوع matris هست. پس تابع add یک نمونه شیئ از نوع matris رو بعنوان پارامتر میگیره و سپس یک خروجی میده که اونهم یک نمونه شیي از نوع matris هست.


]

واقعاً ممنونم توضيح کاملی بود
فقط من اين قسمت بالا رو يکم گيج شدم
مگه matris کلاس نيست چطوری ميشه که به عنوان خروجی و ورودی هم ميشه تو تعریف تابع هم باشه؟

يه سؤال ديگه اگه متغير هامون private بودن اين کلاس چه تغييری ميکرد؟

PC2st
سه شنبه 26 خرداد 1388, 18:37 عصر
مثلا واسه همین کلاس matris و تابع add که توش تعریف شده، به فرض چهار تا ماتریس داریم به نام‌های m1 تا m4 و میخوایم همشون رو با هم جمع کنیم و حاصلش رو تو متغیر m5 بریزیم، میشه اینطور نوشت:


matris m5 = m1.add(m2).add(m3).add(m4);

اولش، متغیر m2 توسط تابع add با متغیر m1 جمع میشه و چون خروجی ناشی از این تابع یک کلاس از نوع matris هست، پس میشه دوباره از تابع add استفاده کرد و سایر متغیرها رو بهش اضافه کنیم و در آخر سر هم نتیجه رو تو متغیر m5 بریزیم. دستور بالا رو میشد اینطور هم نوشت:


matris tmp1 = m1.add(m2);
matris tmp2 = tmp1.add(m3);
matris m5 = tmp2.add(m4);

کلاس matris رو به شیوه‌های بهتری میشد طراحی کرد که برای تشخیص اونها باید به مطالب شی‌گرایی پرداخت که در همین سایت و یا جستجو در گوگل مطالب خوبی احتمالا بشه پیدا کرد. مثلا در اینجا چون متغیر data بصورت اشاره‌گر هست، اگه بخوایم از انتساب (علامت =) برای کپی یک متغیر از نوع matris در متغیر دیگه‌ای استفاده کنیم، احتمالا به مشکل بر بخوریم، مثلا اگه m1 یک نمونه شیئ از نوع matris باشه، دستور زیر مشکل ایجاد میکنه:


matris mm = m1;

چون متغیر data از کلاس matris از نوع اشاره‌گر هست پس یک آدرس در اون ذخیره شده و اطلاعات واقعی در حقیقت تو اون آدرس قرار داره نه در متغیر data... و چون تو تعریف کلاس matris تابعی برای عملیات کپی کردن، پیاده سازی نشده و از تابع پیش‌فرض کپی برای اینکار استفاده شده، پس فقط آدرسی که در متغیر m1.data ذخیره شده در درون متغیر mm.data قرار میگیره در حالی که ما میخواستیم محتویاتی که متغیر data به اون اشاره میکنه، کپی بشه! ولی در عوض فقط آدرس متغیر data عوض شده! پس هر وقت m1.data حذف بشه، مقدار mm.data هم بی‌معنی خواهد بود و برنامه ممکنه با یک خطا به پایان کار خودش ادامه بده.

در مورد اینکه اگه متغیرهامون private بودن، فرقی واسه تابع add نمیکرد بازهم به data دسترسی داشت. ولی برای مقداردهی به اعضای matris دیگه نمیشد مقادیر رو مستقیم به متغیر data ریخت، در اینصورت باید از یک تابع کمکی (بعنوان property در زبانهایی چون سی شارپ) مثلا به اسم set_data استفاده میشد.

adameh_bahal
سه شنبه 26 خرداد 1388, 19:31 عصر
مثلا واسه همین کلاس matris و تابع add که توش تعریف شده، به فرض چهار تا ماتریس داریم به نام‌های m1 تا m4 و میخوایم همشون رو با هم جمع کنیم و حاصلش رو تو متغیر m5 بریزیم، میشه اینطور نوشت:


matris m5 = m1.add(m2).add(m3).add(m4);

اولش، متغیر m2 توسط تابع add با متغیر m1 جمع میشه و چون خروجی ناشی از این تابع یک کلاس از نوع matris هست، پس میشه دوباره از تابع add استفاده کرد و سایر متغیرها رو بهش اضافه کنیم و در آخر سر هم نتیجه رو تو متغیر m5 بریزیم. دستور بالا رو میشد اینطور هم نوشت:


matris tmp1 = m1.add(m2);
matris tmp2 = tmp1.add(m3);
matris m5 = tmp2.add(m4);

کلاس matris رو به شیوه‌های بهتری میشد طراحی کرد که برای تشخیص اونها باید به مطالب شی‌گرایی پرداخت که در همین سایت و یا جستجو در گوگل مطالب خوبی احتمالا بشه پیدا کرد. مثلا در اینجا چون متغیر data بصورت اشاره‌گر هست، اگه بخوایم از انتساب (علامت =) برای کپی یک متغیر از نوع matris در متغیر دیگه‌ای استفاده کنیم، احتمالا به مشکل بر بخوریم، مثلا اگه m1 یک نمونه شیئ از نوع matris باشه، دستور زیر مشکل ایجاد میکنه:


matris mm = m1;

چون متغیر data از کلاس matris از نوع اشاره‌گر هست پس یک آدرس در اون ذخیره شده و اطلاعات واقعی در حقیقت تو اون آدرس قرار داره نه در متغیر data... و چون تو تعریف کلاس matris تابعی برای عملیات کپی کردن، پیاده سازی نشده و از تابع پیش‌فرض کپی برای اینکار استفاده شده، پس فقط آدرسی که در متغیر m1.data ذخیره شده در درون متغیر mm.data قرار میگیره در حالی که ما میخواستیم محتویاتی که متغیر data به اون اشاره میکنه، کپی بشه! ولی در عوض فقط آدرس متغیر data عوض شده! پس هر وقت m1.data حذف بشه، مقدار mm.data هم بی‌معنی خواهد بود و برنامه ممکنه با یک خطا به پایان کار خودش ادامه بده.

در مورد اینکه اگه متغیرهامون private بودن، فرقی واسه تابع add نمیکرد بازهم به data دسترسی داشت. ولی برای مقداردهی به اعضای matris دیگه نمیشد مقادیر رو مستقیم به متغیر data ریخت، در اینصورت باید از یک تابع کمکی (بعنوان property در زبانهایی چون سی شارپ) مثلا به اسم set_data استفاده میشد.

توضيح بسيار کاملی بود بازم ممنونم
فقط اگه ميشه اينکه اگه متغير ها private بشن رو تو همين کلاس به عنوان نمونه نشون بديد

PC2st
سه شنبه 26 خرداد 1388, 22:36 عصر
#include <iostream>
#include <cstdarg>

using namespace std;

class matris
{
private:

double** data;
int rowsize, colsize;

public:

matris (int row, int col)
{
data = new double* [row];
for (int i = 0; i < row; ++i)
{
*(data + i) = new double [col];
}
rowsize = row;
colsize = col;
}

void set_data (int args, ...)
{
int count = args > rowsize * colsize ? rowsize * colsize : args;
int i = 0, j = 0, counter = 0;
va_list params;
va_start (params, args);
while (counter < count)
{
while (j < colsize && counter < count)
{
data [i][j] = va_arg (params, double);
++j;
++counter;
}
j = 0;
++i;
}
va_end (params);
}

double& operator() (int i, int j)
{
if (i < rowsize && j < colsize)
{
return data [i][j];
}
else
{
throw "ERROR";
}
}
};

int main ()
{
matris m1 (3, 4);
m1.set_data (5, 1.0, 2.0, 3.0, 4.0, 5.0);
m1 (1, 1) = 3.14;
m1 (2, 2) = 9.81;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 4; ++j)
{
cout << m1 (i, j) << "\t";
}
cout << endl;
}
}

که برای تابع set_date، پارامتر اول، تعداد عناصری را که می‌خوایم مقداردهی کنیم رو مشخص میکنه و سایر پارامترها هم مقادیر رو مشخص میکنند، مثلا تو مثال بالا در تابع main، به تعداد ۵ عنصر از ماتریس رو مقدار دهی میکنه و چون ابعاد ماتریس ۳ در چهار هست، پس ردیف اول (۴ خانه) و ۱ خانه از ردیف دوم مقدار دهی میگردند. عملگر () هم overload شده تا به سادگی بشه به مقادیر تک تک آرایه‌های ماتریس دسترسی داشت و مقداردهیشون کرد.
واسه پیاده‌سازی تابع set_data از قابلیت توابع variadic (http://en.wikipedia.org/wiki/Variadic_function) استفاده شده اما این شیوه یک ضعف داره و اون اینکه در نوع پارامترهای ورودی کنترلی نمیکنه، مثلا تو تابع set_data حتما باید مقداردهی عناصر ماتریس از نوع double باشه، مثلا اگه شما بجای اعداد اعشاری از اعداد صحیح استفاده کنید، بدون هیچ اخطاری، نتایج غلط می‌گیرید، مثل دستور زیر:


m1.set_data (2, 1, 5);

که باید به اینصورت نوشته بشه:


m1.set_data (2, 1.0, 5.0);

adameh_bahal
چهارشنبه 27 خرداد 1388, 17:12 عصر
به عنوان سؤال آخر
توی اين مثال پايين خروجی تابع set_radious تعريف نشده يعنی به صورت پيش فرض خروجی تابع چی در نظر گرفته ميشه وقتی به جای خروجی تابع هيچی نوشته نميشه؟

بعد من اون قسمت تابع رو متوجه نميشم که this.radious و c1.getradious چجوری مقدار ميگيرن و با هم مقايسه ميشن؟


class circle
{
private:
double x_center,y_center,radious;
public:
set_radious(double r) {radious=r;}
double get_radious(){return radious;}
set_center(double x,double y){x_center=x;y_center=y;}
int compare(circle c1)
{
if this.radious>c1.get_radious() return 1;
if this.radious=c1.get_radious() return 0;
return -1;
}
}
main()
{
circle cir1,cir2;
cir1.set_radious(5);
cir2.set_radious(10);
}

PC2st
چهارشنبه 27 خرداد 1388, 18:16 عصر
چون برنامه نویس نوعی برای خروجی تابع set_radious تعریف نکرده، خروجی از نوع int در نظر گرفته میشه که این کاملا بر خلاف استاندارد زبان ++C بوده و باعث میشه کدهای فوق فقط روی یک کامپایلر بخصوص کامپایل بشه، مثلا من که از GCC استفاده می‌کنم، اجازه چنین کاری رو بهم نمیده و من رو به استفاده از استاندارد ترغیب می‌کنه. مثلا در کدهای شما شرط دستور if درون پرانتز قرار نداره که این هم خلاف استاندارد هست و کاملا غیر حرفه‌ای.

شیئ this به نمونه فعلی اشاره داره، در اینجا this.radious بکار گرفته شده، در صورتیکه this یک اشاره‌گر است و باید بصورت this->radious نوشته میشه. این قسمت کد کاملا غلط و خلاف استاندارد هست، نمیدونم این کد تو چه کامپایلری میتونه کامپایل بشه و چرا چنین اجازه‌ای داده شده!

در هر حال، this->radious مقدار متغیر radious رو میگیره. c1.get_radious نیز به کمک تابع get_radious از شیئ c1 بطور غیر مستقیم مقدار متغیر radious رو از شیئ c1 برمیگردونه. البته چون نوع پارامتر ورودی تابع compare از نوع خود کلاس یعنی circle است، پس بجای c1.get_radious میشد از c1.radious استفاده کرد.

حامد مصافی
چهارشنبه 27 خرداد 1388, 18:23 عصر
محمد جان این کد روی GCC کار نخواهد کرد. پیشوند های set_ و get_ به منظور تعریف یک property مورد استفاده قرار می گیرند. این کد با خانواده MS VC++ همخوانی داره.


بعد من اون قسمت تابع رو متوجه نميشم که this.radious و c1.getradious چجوری مقدار ميگيرن و با هم مقايسه ميشن؟get_radious مقداری را بر می گرداند و radious یک متغیر داخلی است.

PC2st
چهارشنبه 27 خرداد 1388, 18:47 عصر
kه محمد جان این کد روی GCC کار نخواهد کرد. پیشوند های set_ و get_ به منظور تعریف یک property مورد استفاده قرار می گیرند. این کد با خانواده MS VC++ همخوانی داره.
فکر میکنم شما هم از این روش‌های غیر استاندارد ++MS VC دوری خواهید کرد. :لبخند:


چجوری مقدار ميگيرن و با هم مقايسه ميشن؟
الان یک مورد دیگه تو کدها دیدم، خط زیر اشتباه است:


if this.radious=c1.get_radious() return 0;

چون برای مقایسه برابر بودن دو چیز، از علامت == استفاده میشه در حالیکه دستور بالا، مقدار c1.get_radious رو به متغیر this->radious کپی خواهد کرد.

adameh_bahal
چهارشنبه 27 خرداد 1388, 19:34 عصر
چون برنامه نویس نوعی برای خروجی تابع set_radious تعریف نکرده، خروجی از نوع int در نظر گرفته میشه که این کاملا بر خلاف استاندارد زبان ++C بوده و باعث میشه کدهای فوق فقط روی یک کامپایلر بخصوص کامپایل بشه، مثلا من که از GCC استفاده می‌کنم، اجازه چنین کاری رو بهم نمیده و من رو به استفاده از استاندارد ترغیب می‌کنه. مثلا در کدهای شما شرط دستور if درون پرانتز قرار نداره که این هم خلاف استاندارد هست و کاملا غیر حرفه‌ای.

شیئ this به نمونه فعلی اشاره داره، در اینجا this.radious بکار گرفته شده، در صورتیکه this یک اشاره‌گر است و باید بصورت this->radious نوشته میشه. این قسمت کد کاملا غلط و خلاف استاندارد هست، نمیدونم این کد تو چه کامپایلری میتونه کامپایل بشه و چرا چنین اجازه‌ای داده شده!

در هر حال، this->radious مقدار متغیر radious رو میگیره. c1.get_radious نیز به کمک تابع get_radious از شیئ c1 بطور غیر مستقیم مقدار متغیر radious رو از شیئ c1 برمیگردونه. البته چون نوع پارامتر ورودی تابع compare از نوع خود کلاس یعنی circle است، پس بجای c1.get_radious میشد از c1.radious استفاده کرد.

توی مثالی که من دارم this. رو قرار نداده و من خودم نوشتم
ولی گفته شده ميشه قرار هم داد ولی نگفته که اشاره گر هست و تو تعريف شی کلاس بايد از -> به جای . استفاده کرد

adameh_bahal
چهارشنبه 27 خرداد 1388, 19:36 عصر
محمد جان این کد روی GCC کار نخواهد کرد. پیشوند های set_ و get_ به منظور تعریف یک property مورد استفاده قرار می گیرند. این کد با خانواده MS VC++ همخوانی داره.

get_radious مقداری را بر می گرداند و radious یک متغیر داخلی است.

اين زبانی که ما ميخونيم c++ ، درسمون هم طراحی پياده سازی الگوريتم هست

adameh_bahal
چهارشنبه 27 خرداد 1388, 19:39 عصر
فکر میکنم شما هم از این روش‌های غیر استاندارد ++MS VC دوری خواهید کرد. :لبخند:


الان یک مورد دیگه تو کدها دیدم، خط زیر اشتباه است:


if this.radious=c1.get_radious() return 0;

چون برای مقایسه برابر بودن دو چیز، از علامت == استفاده میشه در حالیکه دستور بالا، مقدار c1.get_radious رو به متغیر this->radious کپی خواهد کرد.

متاسفانه يا خوشبختانه استاد ما زياد اعتقاد به نوشتن کد به صورت کامل نداره و بيشتر درس اون جلسه براش مهمه
من کلاً تو جزوه از اين ايردها زياد دارم
برا همين اينقدر حتی تو چيزای کوچيک به مشکل خوردم

يه سؤال ديگه که نميدونم مشکل منه يا جزوه:
چرا اصلاً از cin و cout استفاده نشده؟ لازم نيست نوشته بشه يا اينکه اينجا نوشته نشده؟

PC2st
چهارشنبه 27 خرداد 1388, 20:12 عصر
متاسفانه يا خوشبختانه استاد ما زياد اعتقاد به نوشتن کد به صورت کامل نداره و بيشتر درس اون جلسه براش مهمهپس مشکل ((متاسفانه)) از این ناشی میشه...


چرا اصلاً از cin و cout استفاده نشده؟ لازم نيست نوشته بشه يا اينکه اينجا نوشته نشده؟
cin و cout که اولی برای گرفتن مقادیر از کاربر (از ترمینال) و دومی برای نمایش اطلاعات به کاربر (در ترمینال یا command prompt ویندوز) استفاده میشه. لابد نمی‌خواسته تا با کاربر تعاملی داشته باشه! تا سوت و کور برنامه کار خودش رو انجام بده!

adameh_bahal
چهارشنبه 27 خرداد 1388, 21:35 عصر
پس مشکل ((متاسفانه)) از این ناشی میشه...


cin و cout که اولی برای گرفتن مقادیر از کاربر (از ترمینال) و دومی برای نمایش اطلاعات به کاربر (در ترمینال یا command prompt ویندوز) استفاده میشه. لابد نمی‌خواسته تا با کاربر تعاملی داشته باشه! تا سوت و کور برنامه کار خودش رو انجام بده!

آخه تو خيلی از توابع ورودی نياز هست چجوری نياز به cin پيدا نميشه؟

PC2st
پنج شنبه 28 خرداد 1388, 01:35 صبح
بله، شما درست می‌فرمایید، تو خیلی از برنامه‌ها نیاز به ورودی هست. اصولا اغلب برنامه‌ها رو واسه این نوشتن که ورودی بگیره، اطلاعات رو تجزیه تحلیل و پردازش کنه، بعدش خروجی بده. اون حرف من از روی شوخی و مثلا مزاح بود! :لبخند: و منظورم این بود که اگه تو جزوه، برنامه‌ها از cin و cout استفاده نکردند، پس کاربردی هم ندارند و بدون تعامل با کاربر و بطور سوت و کور، کار خودشون رو بدون هیچ نتیجه‌ای (که برای ما سودمند باشه) انجام می‌دهند. اما در هر حال، بطور کلی، برنامه می‌تونه بدون ورودی باشه، مثل برنامه محافظ صفحه نمایش (screensaver)، مثل برنامه Hello World یا برنامه چاپ ستاره... و برنامه می‌تونه بدون خروجی هم باشه، مثل برنامه‌ای که وظیفه‌اش ایجاد یک توقف چند ثانیه‌ای در روند اجرای سیستم هست که کاربر تعداد ثانیه رو بعنوان ورودی میده (مثل دستور sleep در سیستم عامل‌های خانواده Unix.) یا برنامه‌ای که بعضی فایل‌های غیر ضروری روی سیستم رو پاک می‌کنه، مثل برنامه‌ی مخربی که وظیفه‌اش تحمیل پردازش‌های اضافی و بی‌خود به CPU است (چنین چیز مخربی هست حالا؟!). یا مثل برنامه‌ای که مجوز اجرای یک فایل (برنامه) رو بررسی کنه، اگه اجازه داشت برنامه اجرا میشه در غیر اینصورت نه. اما بستگی داره که منظور ما از خروجی چی باشه؟ شاید کسی حذف کردن یک سری فایل توسط یک برنامه رو بعنوان خروجی اون برنامه در نظر بگیره چون اعتقاد داره که خروجی یک برنامه، همون نتایح ناشی از پردازش‌های برنامه است پس در اینصورت به سختی می‌شه برنامه‌ی کاربردی که بدون خروجی باشه رو پیدا کرد.

adameh_bahal
پنج شنبه 28 خرداد 1388, 10:02 صبح
بله، شما درست می‌فرمایید، تو خیلی از برنامه‌ها نیاز به ورودی هست. اصولا اغلب برنامه‌ها رو واسه این نوشتن که ورودی بگیره، اطلاعات رو تجزیه تحلیل و پردازش کنه، بعدش خروجی بده. اون حرف من از روی شوخی و مثلا مزاح بود! :لبخند: و منظورم این بود که اگه تو جزوه، برنامه‌ها از cin و cout استفاده نکردند، پس کاربردی هم ندارند و بدون تعامل با کاربر و بطور سوت و کور، کار خودشون رو بدون هیچ نتیجه‌ای (که برای ما سودمند باشه) انجام می‌دهند. اما در هر حال، بطور کلی، برنامه می‌تونه بدون ورودی باشه، مثل برنامه محافظ صفحه نمایش (screensaver)، مثل برنامه Hello World یا برنامه چاپ ستاره... و برنامه می‌تونه بدون خروجی هم باشه، مثل برنامه‌ای که وظیفه‌اش ایجاد یک توقف چند ثانیه‌ای در روند اجرای سیستم هست که کاربر تعداد ثانیه رو بعنوان ورودی میده (مثل دستور sleep در سیستم عامل‌های خانواده Unix.) یا برنامه‌ای که بعضی فایل‌های غیر ضروری روی سیستم رو پاک می‌کنه، مثل برنامه‌ی مخربی که وظیفه‌اش تحمیل پردازش‌های اضافی و بی‌خود به CPU است (چنین چیز مخربی هست حالا؟!). یا مثل برنامه‌ای که مجوز اجرای یک فایل (برنامه) رو بررسی کنه، اگه اجازه داشت برنامه اجرا میشه در غیر اینصورت نه. اما بستگی داره که منظور ما از خروجی چی باشه؟ شاید کسی حذف کردن یک سری فایل توسط یک برنامه رو بعنوان خروجی اون برنامه در نظر بگیره چون اعتقاد داره که خروجی یک برنامه، همون نتایح ناشی از پردازش‌های برنامه است پس در اینصورت به سختی می‌شه برنامه‌ی کاربردی که بدون خروجی باشه رو پیدا کرد.

الان توی همين توابع مگه وقتی ورودی داريم نبايد از cin استفاده کنيم؟ و وقتی هم خروجی داريم از cout ؟ ولی تو همين 2 تا مثال که گزاشتم استفاده نشده

PC2st
پنج شنبه 28 خرداد 1388, 13:49 عصر
دقیقا نمی‌دونم سوال شما رو متوجه شدم یا نه، بهر حال:


اگه منظور شما تاکید روی cin و cout باشه، در اینصورت جواب بستگی داره.
که احتمالا منظور شما این نیست :شیطان: اما کار از محکم کاری عیب نمی‌کنه (!)
درسته، cin ورودی استاندارد و cout هم خروجی استاندارد هست. ورودی استاندارد می‌تونه صفحه کلید (از خط فرمان) یا محتویات یک فایل یا غیره باشه و خروجی استاندارد هم می‌تونه ترمینال (خط فرمان) یا باز هم یک فایل یا غیره باشه. که بطور پیش فرض، ورودی استاندارد صفحه کلید (از ترمینال) و خروجی استاندارد هم صفحه نمایش (در ترمینال) هست. اما به این معنی نیست که برای هر ورودی یا خروجی از cin و cout استفاده می‌کنیم.
اگه ورودی رو بخوایم از کاربر بگیریم، آره از cin و برای نمایش خروجی در ترمینال هم از cout استفاده می‌کنیم. اما اگه خروجی یا ورودی ما یک فایل باشه، از fstream ها استفاده می‌کنیم و اگه این فایل بعنوان ورودی یا خروجی استاندارد تعریف شده باشه از cin یا cout هم میشه استفاده کرد ولی file stream ها کاری به تغییرات ورودی استاندارد یا خروجی استاندارد نداره و فایلی رو که ما بهش دادیم مورد استفاده قرار می‌ده. بگذارید یک مثال بزنم، فرضا برنامه ggopp سه رشته متن از کاربر می‌گیره و اون‌ها رو در داخل یک پیغام قرار میده و چاپ می‌کنه، اگه در لینوکس دستور زیر رو بزنیم:


cat my-filename | ggopp

دستور cat محتویات فایل my-filename رو میخونه و بعنوان ورودی به برنامه ggopp میده. چه اتفاقی افتاده؟ ورودی برنامه ggopp تغییر کرده، دیگه این برنامه سه رشته متن رو بطور مستقیم از ما نمی‌گیره و بلکه این سه رشته متن رو از محتویات فایلی میگیره که توسط دستور cat گرفته شده.
همانطور که اطلاع دارید، با ++C میشه برای واسط گرافیکی کاربر (GUI) هم برنامه‌نویسی کرد (برنامه‌ای چون Notepad ویندوز نوشت) و برای اینکار از win32 API یا از کتابخونه‌هایی چون MFC یا Qt یا GTKmm یا FLTK یا غیره میشه استفاده کرد، در اینصورت اگه کاربر مثلا یک کلید رو فشار بده (انتخاب بین Yes یا No) پس ورودی ما از اینجا تامین شده یا مثلا وارد کردن تاریخ در یک جعبه متنی (Text Box)...
و اگه بخوایم ورودی رو بصورت پارامتر از کاربر بگیریم، از پارامتر argv (پارامتر دوم تابع main) استفاده می‌کنیم:


int main (int args, char** argv)
{
...
}

اگه منظور شما این باشه که بطور کلی، برنامه‌هایی که اینجا در موردش بحث شد باید با کاربر تعامل داشته باشه و ورودی و خروجی داشته باشه، آره بهتر بود که از cin یا cout هم استفاده می‌شد. که احتمالا منظور شما هم همین بوده و چیزهایی که بالا نوشتم واسه خودم بوده :لبخند:

adameh_bahal
پنج شنبه 28 خرداد 1388, 14:26 عصر
دقیقا نمی‌دونم سوال شما رو متوجه شدم یا نه، بهر حال:


اگه منظور شما تاکید روی cin و cout باشه، در اینصورت جواب بستگی داره.
که احتمالا منظور شما این نیست :شیطان: اما کار از محکم کاری عیب نمی‌کنه (!)
درسته، cin ورودی استاندارد و cout هم خروجی استاندارد هست. ورودی استاندارد می‌تونه صفحه کلید (از خط فرمان) یا محتویات یک فایل یا غیره باشه و خروجی استاندارد هم می‌تونه ترمینال (خط فرمان) یا باز هم یک فایل یا غیره باشه. که بطور پیش فرض، ورودی استاندارد صفحه کلید (از ترمینال) و خروجی استاندارد هم صفحه نمایش (در ترمینال) هست. اما به این معنی نیست که برای هر ورودی یا خروجی از cin و cout استفاده می‌کنیم.
اگه ورودی رو بخوایم از کاربر بگیریم، آره از cin و برای نمایش خروجی در ترمینال هم از cout استفاده می‌کنیم. اما اگه خروجی یا ورودی ما یک فایل باشه، از fstream ها استفاده می‌کنیم و اگه این فایل بعنوان ورودی یا خروجی استاندارد تعریف شده باشه از cin یا cout هم میشه استفاده کرد ولی file stream ها کاری به تغییرات ورودی استاندارد یا خروجی استاندارد نداره و فایلی رو که ما بهش دادیم مورد استفاده قرار می‌ده. بگذارید یک مثال بزنم، فرضا برنامه ggopp سه رشته متن از کاربر می‌گیره و اون‌ها رو در داخل یک پیغام قرار میده و چاپ می‌کنه، اگه در لینوکس دستور زیر رو بزنیم:


cat my-filename | ggopp

دستور cat محتویات فایل my-filename رو میخونه و بعنوان ورودی به برنامه ggopp میده. چه اتفاقی افتاده؟ ورودی برنامه ggopp تغییر کرده، دیگه این برنامه سه رشته متن رو بطور مستقیم از ما نمی‌گیره و بلکه این سه رشته متن رو از محتویات فایلی میگیره که توسط دستور cat گرفته شده.
همانطور که اطلاع دارید، با ++C میشه برای واسط گرافیکی کاربر (GUI) هم برنامه‌نویسی کرد (برنامه‌ای چون Notepad ویندوز نوشت) و برای اینکار از win32 API یا از کتابخونه‌هایی چون MFC یا Qt یا GTKmm یا FLTK یا غیره میشه استفاده کرد، در اینصورت اگه کاربر مثلا یک کلید رو فشار بده (انتخاب بین Yes یا No) پس ورودی ما از اینجا تامین شده یا مثلا وارد کردن تاریخ در یک جعبه متنی (Text Box)...
و اگه بخوایم ورودی رو بصورت پارامتر از کاربر بگیریم، از پارامتر argv (پارامتر دوم تابع main) استفاده می‌کنیم:


int main (int args, char** argv)
{
...
}

اگه منظور شما این باشه که بطور کلی، برنامه‌هایی که اینجا در موردش بحث شد باید با کاربر تعامل داشته باشه و ورودی و خروجی داشته باشه، آره بهتر بود که از cin یا cout هم استفاده می‌شد. که احتمالا منظور شما هم همین بوده و چیزهایی که بالا نوشتم واسه خودم بوده :لبخند:


درسته منظور من همون دومی بود
واقعاً ممنونم توضيحات شما بسيار کامله از اينکه وقت ميزاريد و پاسخ ميديد بسيار سپاسگزارم