PDA

View Full Version : سوالی پیرامون استفاده از کلاس بدون ایجاد نمونه



tux-world
جمعه 10 مرداد 1393, 11:31 صبح
سلام دوستان. من بیشتر از بقیه زبانهای برنامه نویسی PHP کار کردم. تو این مثالی که آقای اسد زاده نوشته بودن سوالی ذهنمو مشغول کرد.
#include <iostream>
using namespace std;
class Line
{
public:
void setLength(double len);
double getLength(void);
Line(); // This is the constructor declaration
~Line(); // This is the destructor: declaration

private:
double length;
};

// Member functions definitions including constructor
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}

void Line::setLength(double len)
{
length = len;
}


double Line::getLength(void)
{
return length;
}
// Main function for the program
int main()
{
Line line;

// set line length
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;

return 0;
}

ما تو PHP بخواییم از یه کلاس بدون اینکه ازش نمونه بسازیم میتونیم توسط یکی از توابعش یه نمونه داخل همون کلاس ایجاد کنیم. مثلا:



class Permission
{
protected $id = 0;
public function __construct($id)
{
$this->id = $id;
}
public static function user( $id )
{
return new static($id);
}
}
$bar = Permission::user(100)->item("HELLO")->printCheck();


الان منظور از تعریفات توابعی مثل:

Line::Line(void);
Line::~Line(void);
void Line::setLength(double len);


چی هستش؟ چرا اسم کلاس رو ما ابتدای توابع میاریم؟ در صورتی که تو PHP و Java همچین چیزی ندیدم.

returnx
جمعه 10 مرداد 1393, 15:38 عصر
این داستان بطور کل برای دو موضوع اتفاق میفته ، یکی پیاده سازی متد های عضو یک کلاس ، 2 دسترسی به اعضای استاتیک یک کلاس...
وقتی شما یک کلاس می سازید میتونید پیاده سازی متدهاشُ به چند طریق انجام بدید :
روش اول (روش سنتی) :
در این روش پیاده سازی متد ها در کلاس انجام میشه :
class test
{
public :
int sum(int a,int b)
{
return a+b;
}
};
روش دوم :
در این روش کلاس و پیاده سازی متد ها در یک فایل قرار دارند ، اما پیاده سازی متد ها خارج از کلاس نوشته میشه و در کلاس فقط الگوی متد را خواهیم داشت در این روش نام متد معتبر نخواهد بود مگر اینکه نام کلاس را قبل از نام متد بیاریم :
class test
{
public :
int sum(int a,int b);
};

int test::sum(int a, int b)
{
return a+b;
}

روش سوم :
در این روش الگوی کلاس در یک فایل هدر نوشته میشه و پیاده سازی متد ها در فایل دیگری قرار می گیرند :
محتوای فایل هدر:
class test
{
public :
int sum(int a,int b);
};

محتوای فایل cpp :
int test::sum(int a, int b)
{
return a+b;
}
در این روش هم برای پیاده سازی متد ها می بایست نام کلاس قبل از نام متد آورده شود.

اما موضوع دوم :
در این موضوع ما متد هایی را به صورت static تعریف می کنیم ، برای دسترسی به متد های استاتیک نیاز نیست که از کلاس یک شی ساخته بشه ، فقط کافیه نام کلاس را قبل از نام متد بیاریم به این روش :
class test
{
public :
static int sum(int a,int b);
};

int test::sum(int a, int b)
{
return a+b;
}
نحوه صدا زدن متد :
cout<<test::sum(5,2);

tux-world
جمعه 10 مرداد 1393, 22:24 عصر
توضیحات بسیار قشنگی دادید سپاسگذارم. یه سوال دیگه هم داشتم. اینه که چطور در ++C میتونیم زنجیره ای از توابع رو همانند این کد در PHP تعریف کنیم؟



Permission::user(100)->item("HELLO")->printCheck();


ما اینجا با یه نمونه ای که داخل کلاس ایجاد کردیم تو هر تابع با بازگردوندن شیئ به توابع دیگه دسترسی داریم

returnx
جمعه 10 مرداد 1393, 23:58 عصر
در C++‎‎‎ هم تقریبا به همان شکل هست ، کافیه مقداری که یک متد بر میگردونه از نوع یک کلاس دیگر باشه ، اون موقع در زمان صدا زدن متد کلاس اول به متد های کلاسی که بر گشت داده میشه هم دسترسی خواهیم داشت به مثال زیر توجه کنید :

class foo
{
public :
foo () {}
int sum(int a,int b)
{
return a+b;
}

};

class bar
{
public:
bar() {}
foo get_test()
{
foo i_test;
return i_test;
}
};
صدا زدن :
bar i_bar;
cout<<i_bar.get_test().sum(5,4);

tux-world
شنبه 11 مرداد 1393, 00:20 صبح
منظورم تو یه کلاس بود. کلاس تو کلاس نه. این مثال شما تو PHP هم امکان پذیره

rahnema1
شنبه 11 مرداد 1393, 13:42 عصر
منظورم تو یه کلاس بود. کلاس تو کلاس نه. این مثال شما تو PHP هم امکان پذیره

سوال شما واضح نیست بر اساس حدسی که زدم این جواب میشه

#include <string>
#include <iostream>
using namespace std;

struct Permission{


static Permission user(int _id)
{
Permission p;
return p ;
}
Permission& item(string _item)
{
return (*this);
}

Permission& printCheck()
{
return (*this);
}

};
int main()
{
Permission::user(100).item("HELLO").printCheck();
}

tux-world
شنبه 11 مرداد 1393, 15:37 عصر
آره درسته یه همچین چیزی منظورم بود. الان منظور از & بعد توابع منظورتون جایی که فانکشن تعریف میشه ارجاع میده؟ یعنی منظور ارجاع هستش؟

rahnema1
شنبه 11 مرداد 1393, 17:57 عصر
آره درسته یه همچین چیزی منظورم بود. الان منظور از & بعد توابع منظورتون جایی که فانکشن تعریف میشه ارجاع میده؟ یعنی منظور ارجاع هستش؟

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

#include <string>
#include <iostream>
using namespace std;

struct Permission{
static Permission user(int _id)
{
Permission p;
cout<<&p<<endl;
return p ;
}
Permission& item(string _item)
{
cout<<this<<endl;
return (*this);
}
Permission& printCheck()
{
cout<<this<<endl;
return (*this);
}

};
int main()
{
Permission::user(100).item("HELLO").printCheck();
}

در مورد تابع اول چون شیء جدید ایجاد میشه نباید رفرنس اون شیء محلی را به بیرون فرستاد در نتیجه ما در تابع اولی کپی انجام می دهیم
اما برای جلوگیری از این کار می تونید از یک نوع ارجاع دیگه به نام rvalue reference به همراه move استفاده کنیم

static Permission&& user(int _id)
{
Permission p;
cout<<&p<<endl;
return move(p) ;
}

tux-world
شنبه 11 مرداد 1393, 22:38 عصر
جالب شود قضیه برام. الان تو تابع یوزر که دو تا && داره منظورتون رو از نفرستادن رفرنس به بیرون رو متوجه نمیشم. یعنی اینکه اگه از این کلاس مشتق بشه و یا به نوعی استفاده بشه؟ بعد اینکه عملیات کپی انجام میشه این کپی هستش که هر بار تو توابع پاس داده میشه درسته؟

rahnema1
شنبه 11 مرداد 1393, 22:47 عصر
جالب شود قضیه برام. الان تو تابع یوزر که دو تا && داره منظورتون رو از نفرستادن رفرنس به بیرون رو متوجه نمیشم. یعنی اینکه اگه از این کلاس مشتق بشه و یا به نوعی استفاده بشه؟ بعد اینکه عملیات کپی انجام میشه این کپی هستش که هر بار تو توابع پاس داده میشه درسته؟

منظورم جلوگیری از کپی بود ما با استفاده از رفرنس از کپی شدن ( با هزینه هایش) جلوگیری می کنیم.

tux-world
شنبه 11 مرداد 1393, 22:51 عصر
متوجه نشدم اصلا منظورتون چیه. مخصوصا && رو و نفرستادن رفرنس

rahnema1
شنبه 11 مرداد 1393, 22:59 عصر
متوجه نشدم اصلا منظورتون چیه. مخصوصا && رو و نفرستادن رفرنس

وقتی توی یک تابع متغیر محلی تعریف می کنیم اون متغیر مدت زمانی که در حافظه باقی خواهد ماند تا انتهای تابع هست و وقتی تابع تموم شد از بین میره در نتیجه اگر رفرنس اون را return کنیم در واقع رفرنس به چیزی هست که دیگه وجود نداره

2020s1371
شنبه 11 مرداد 1393, 23:56 عصر
اگه میشه این رو اول برام توضیح بدین ک دقیقا چجوری عمل میکنه؟؟
Permission::user(100).item("HELLO").printCheck();

دو:
من مرجع به عنوان بازگشتی رو درست حسابی متوجه نمیشم
مثال:


#include <iostream>


using namespace std;
char &replace(int i);
char s[50]="hello there";
int main()
{
replace(5)='X';
cout<<s<<endl;
return 0;
}
char &replace(int i){
return s[i];
}

خروجیش هم اینه: helloXthere

لطفا اینا تحلیل کنین ک چ اتفاقی میفته دقیقا؟؟

rahnema1
یک شنبه 12 مرداد 1393, 08:48 صبح
فکر کنم اول سوال دوم را توضیح بدم بهتر باشه
قبل از این به مفهوم «رسته ی مقداری» یا value category عبارتها بپردازیم
تا قبل از c++11 عبارتها به دو رسته lvalue و rvalue تقسیم بندی می شدند که فعلا همین را توضیح می دهیم
شما عبارت ساده 2+a=1 را در نظر بگیرید که سمت چپ مساوی یک عبارت داریم وسمت راست اون هم یک عبارت
خاصیت عبارت سمت چپ (a) اینه که در حافظه یه جا (آدرس) مشخص واسه خودش داره و می تونیم مقدار اون را آپدیت کنیم
به عنوان یک مثال دیگه int a=1;++a=1+2; را در نظر بگیرید. چون a عبارت است از lvalue ، قبل از این که عبارت سمت راست بهش نسبت داده بشه میشه هر بلایی سرش در آورد یعنی یک مقدار را بهش اضافه کرد. به عبارت دیگه ابتدا a برابر با 2 میشه و سپس 3 با اون جمع میشه که میشه 5
اما عبارت سمت راست جای مشخصی در حافظه نداره که بشه بهش اشاره کرد و اون را آپدیت کرد مثلا این عبارت int a=1;a=(1+2)++; را ببینید که امکان پذیر نیست
به اولی lvalue می گیم و به دومی rvalue می گیم که حرف اول اون کلمه ها به left و right اشاره می کنه
بنابراین در ذهن خودتون اینجور در نظر بگیرید که هر عبارت عددی که بشه با ++ اون را آپدیت کرد lvalue و در غیر اینصورت rvalue هست ( البته این را بعنوان مثال میگم چون ممکنه عبارت شامل عدد نباشه )
همچنین هر چیز موقتی rvalue هست مثلا تابع زیر را در نظر بگیرید

int pow2(int a)
{
return a*a;
}
int main()
{
int b=pow2(3)++;
}

نتیجه تابع pow2 موقتی هست و جای مشخصی در حافظه نداره که بشه آپدیت کرد



حالا می پردازیم به رفرنس یا ارجاع این مثال را ببینید

int a=1;
int &b=a;
b++;
cout<<a;

b به a ارجاع میده و اگه b تغییر کنه a هم تغییر می کنه
حالا این مثال را ببینید

int w=5;
void tagheer()
{
int &k=w;
k++;
}
int main()
{
tagheer();
cout<< w;
}

باز هم همون اتفاق می افته با این تفاوت که k که یک رفرنس باشه در یک مدت کوتاه استفاده میشه و کار خودش را می کنه و بعد از صحنه محو می شه
در مثال تابع pow2 دیدیم که نتیجه اون rvalue بود اما در استاندارد عنوان کرده که علاوه بر اشیاء ( مانند متغیرها) تابع ها هم می تونند lvalue باشند
حالا بر می گیردیم به مثالی که شما زدید
برنامه شما را به این صورت میشه نوشت

char s[50]="hello there";
int i=5;
void tagheer()
{
char &k= s[i];
k='X';
}
int main()
{
tagheer();
cout<<s;
}

یعنی شما( replace(5 را مانند k در نظر بگیرید که بعد از استفاده از صحنه روزگار محو میشه
این هم چند تا مثال دیگه

int _a=1;
int& a()
{
return _a;
}
int main()
{
a()++;
}
یا
int& khodam(int& a)
{
return a;
}
int main()
{
int b=1;
khodam(b)++;
}


در مورد سوال دوم هم با توجه به نکات بالا فکر کنم موضوع روشن شده باشه فقط در مورد this این نکته هست که this اشاره گری به شیء جاری بر می گردونه به عبارت دیگه this یعنی «خودم» سوال را دقیقا بگید کجاش مشکل هست

نکته دیگه ما در بالا هر وقت از رفرنس گفتیم منظورم lvalue reference بود. چون دو نوع رفرنس یا ارجاع داریم یکی lvalue reference و دیگری rvalue reference که اولی با & و دومی با && مشخص میشه همچنین از c++11 به بعد rvalue , lvalue شده lvalue,xvalue,prvalue

omid_kma
چهارشنبه 15 مرداد 1393, 23:05 عصر
در مورد تابع اول چون شیء جدید ایجاد میشه نباید رفرنس اون شیء محلی را به بیرون فرستاد در نتیجه ما در تابع اولی کپی انجام می دهیم
اما برای جلوگیری از این کار می تونید از یک نوع ارجاع دیگه به نام rvalue reference به همراه move استفاده کنیم

static Permission&& user(int _id)
{
Permission p;
cout<<&p<<endl;
return move(p) ;
}



سلام این کد علاوه بر این که undefine behavior هستش(برگرداندن مقدار temp با refrence ) از RVO و بهینه سازی کامپایلر هم جلوگیری می کنه .

rahnema1
پنج شنبه 16 مرداد 1393, 08:55 صبح
سلام این کد علاوه بر این که undefine behavior هستش(برگرداندن مقدار temp با refrence ) از RVO و بهینه سازی کامپایلر هم جلوگیری می کنه .همون بهتر که به قول شما بذاریم کامپایلر rvo خودش را انجام بده. ممنون

rahnema1
پنج شنبه 16 مرداد 1393, 09:23 صبح
البته الان دارن در مورد ابهامات temporary مطالبی را به استاندارد جدید اضافه می کنند
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3918.html