برگرداندن از تابع با مقدار یا ارسال به تابع با refrence - هفت خط کد انجمن پرسش و پاسخ برنامه نویسی

وبـــلاگ هــفت خــط کــد


آموزش های برنامه نویسی
۳۵۷ نفر آنلاین
۱۳۹ عضو و ۲۱۸ مهمان در سایت حاضرند

برگرداندن از تابع با مقدار یا ارسال به تابع با refrence

+3 امتیاز

سلام دوستان

 اگر مثلا تابع زیر را داشته باشم :

std::vector<int> func()
{
    std::vector<int> a;
    //anjam amalaiat rooye a
    return a;
}

آیا این return کردن وکتور باا مقدار بهینست ؟  

آخه تا اون جایی که اطلاع دارم از c++11 بخاطر move semantic دیگه چزی کپی نمیشه درسته؟

یا بهتره به شکل زیر  بنویسم ؟؟

void foo(std::vector<int>& a)
{
       //anjam amaliat rooye a...
}

 

سوال شده مرداد 17, 1393  بوسیله ی sailent (امتیاز 355)   16 44 59
دوباره تگ گذاری شد مرداد 17, 1393 بوسیله ی BlueBlade

1 پاسخ

+2 امتیاز
 
بهترین پاسخ

 بهتره از کد اول که وکتور return میشه استفاده کرد چون خواناتر هست و اکثر اوقات هیچ سربار خاصی نداره و معادل فرستادن با refrence هست .

دلیل این که این 2  مورد اکثر اوقات  با هم تفاوتی ندارن برمیگرده به  بهینه سازی های مربوط به کامپایلر ( RVO , NRVO )

زمانی که یک مقدار از تابع برگشت داده میشه 2 تا کپی گرفته میشه :

1_کپی مقدار در حال بازگشت به یک فضای موقت 

2_ کپی شدن مقدار از فضای موقت به جایی که از اون جا تابع صدا زده شده .

int foo()
{
    return 1; //inja 1 copy anjam mishe
}

int c=foo();//inja copy dovom

کامپایلر ها 2 روش بهینه سازی مربوط به مقدار برگشتی از توابع دارن RVO , NRVO .

RVO مربوط میشه به حذف مورد اول  که خیلی وقته کامپایلر ها این مورد رو پشتیبانی می کنن و تقریبا همه جا این کپی اضافی حذف میشه .

NRVO مربوط میشه به حذف کپی دوم 

برای این که NRVO و RVO انجام بشه موارد زیر باید رعایت بشه : 

1_تابع باید  از یک جا return بشه 

int foo()//NRVO fail
{
    if(1) 
         return 1;
     return 2;
}

2_بصورتی شرطی نباید return بشه 

int foo()//NRVO fail
{
    bool cond;
   return cond?1:2;
}

3_سازنده مقداری که بر میگرده نباید side effect داشته باشه 

مثلا وقتی که داخل سازنده شی ای که بر میگردونیم  از cout استفاده کنیم یا حافظه بگیریم(البته این مورد بستگی به کامپایلر داره خیلی وقت ها بازم حذف میشه )

 ضمنا NRVO,RVO قبل از c++11 هم وجود داشتن .

 

حالا به هر دلیلی اگر NRVO,RVO انجام نشه اگر کلاس move constructor  و move assignment operator داشته باشه زمان return شدن از این 2 استفاده میشه(اگر نداشته باشه کپی انجام میشه ) 

move  از کپی کردن به مراتب سریع تر هستش ولی از فرستادن با refrence کمی کند تر (البته بستگی به نحوه پیاده سازی کلاس هم داره )

 

در مثالی که خودتون زدید اگر با refrence فرستاده بشه زمان صدا زده شدن تابع  فقط یک آدرس 4 یا 8 بایتی کپی میشه 

void foo(std::vector<int>& a/* inja 1 address copy mishe */)
{
       //anjam amaliat rooye a...
}

ولی اگر return بشه و move صدا زده بشه کدی مثل کد زیر  اجرا میشه :(کد از هدر vector ویژوال استودیو )

void _Assign_rv(_Myt&& _Right, true_type)
{	// move from _Right, stealing its contents
	this->_Swap_all((_Myt&)_Right);//swap all iterators
	this->_Myfirst = _Right._Myfirst;
	this->_Mylast = _Right._Mylast;
	this->_Myend = _Right._Myend;

	_Right._Myfirst = pointer();
	_Right._Mylast = pointer();
	_Right._Myend = pointer();
}

داخل کد بالا خود Swap_all هم چند تا pointer رو کپی میکنه .

یعنی اگر move استفاده بشه زمان return شدن به نسبت زمانی که با آدرس به تابع میفرستیم چند تا پوینتر بیشتر کپی میشه. که  عملیات خیلی سریعیه و در صورتی که این تابع چند میلیون بار پشت سر هم اجرا نشه میشه ازش صرف نظر کرد.

پس در مجموع میشه گفت این 2 مورد تفاوتی زیادی با هم ندارن  و بعضی وقت ها فرستادن با refrence  کمی سریع تره .

پاسخ داده شده مرداد 17, 1393 بوسیله ی BlueBlade (امتیاز 15,315)   15 18 89
انتخاب شد مرداد 22, 1393 بوسیله ی Ali Rahbar
...