میخواستم بدونم عملکرد unique_ptr و decltype چی هست و هر کدوم چه وظیفه ای رو دارن؟
unique_ptr یک اشاره گر هوشمند هست. اشاره گرهای هوشمند به گونه ای طراحی شده اند که از لحاظ حافظه و عملکرد به همان اندازه کارآمد باشند.
واز اونجا که یکتا هستند نمی تونید کپی ازشون بگیرید فقط می تونید به یک اشاره گر هوشمند دیگه منتقل کنید که در اونصورت اشاره گر اصلی reset میشه.
به عنوان مثال، تنها عضو داده در unique_ptr، نشانگر خام محصور شده در ویلدکاردها است. به این معنی است که unique_ptr دقیقا به همون اندازه اشاره گرخام، چهار بایت یا هشت بایت است.
پس در جمع بندی داریم ....
- unique_ptr اشاره گر آن را به اشتراک نمی گذارد.
- نمیتواند به یک unique_ptr دیگر از طریق مقدار به یک تابع منتقل شود.
- در هر الگوریتم STL (Standard Template Library) که نیاز به کپی هایی دارد، مورد استفاده قرار میگیرد.
- unique_ptr تنها می تواند منتقل شود این به این معنی است که مالکیت منابع حافظه به unique_ptr منتقل شده است و unique_ptr اصلی دیگر آن را ندارد.
از اونجایی که مالکیت چندگانه منطق برنامه را دچار پیچیدگی می کنه بنابراین هنگامی که نیاز به اشاره گر برای یک شی ساده ++C دارید، از unique_ptr استفاده کنید، و هنگام ساخت یک unique_ptr، از تابع helper make_unique استفاده کنید.
مثلا من از یه اسکوپ خارج بشم چطور متوجه میشه و اون شی رو delete میکنه؟ ممکنه حالتی پیش بیاد که memory leak پیش بیاد با این روش؟
همانطور که در مثال زیر نشان داده شده، نشانگر هوشمند، یک الگوی کلاس Template Class است که شما در پشته آن را اعلام می کنید و با استفاده از نشانگر خام که به کلاس person اشاره داره مقدار اولیه را تعیین می کنید.یعنی اشاره گر هوشمند مسئول حذف حافظه اشاره گر خام است. مخرب اشاره گر هوشمند شامل فراخوانی برای حذف است. زمانی که نشانگر هوشمند از محدوده خارج می شود، مخرب آن فراخوانی می شود، حتی اگر یک استثنا تولید بشه.
void UseRawPointer()
{
// Declare a raw pointer on heap -- not recommended.
Person* pPerson = new Person(L"Farhad", L"Shiri");
// Use pPerson pointer...
// Don't forget to delete!
delete pPerson;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Person> spPerson(new Person(L"Farhad", L"Shiri"));
// Use spPerson smart pointer...
string s = spPerson2->name;
//...
} // spPerson is deleted automatically here.
یعنی میتونیم بگیم جایگزین garbage collector در جاوا میتونه باشه؟
نشانگر هوشمند ++C شبیه ساختن شی در زبان هایی مانند #C است. شی را ایجاد می کنید و سپس سیستم اون به موقع حذف می کنه.
تفاوت در این است که هیچ جمع کننده زباله جداگانه در پس زمینه اجرا نمی شود؛ حافظه از طریق قوانین محدوده ++C استاندارد مدیریت می شود تا محیط کاری سریع تر و کارآمدتر باشد.
اگر فرضا داخل آبجکت ساخته شده از این کلاس به نام x مثلا یک آبجکتی باشه به نام y (یعنی y درون x قرار داره) و یه آبجکت دیگه به نام z به آبجکت y اشاره کنه، با مخرب اتوماتیک x آیا اون آبجکت درونی (y) حذف میشه یا رفرنس هاش شمرده میشن؟
ضمنا آیا لازمه کارکرد صحیح، این هست که کلاس هایی که در این کلاس مورد استفاده قرار گرفتن هم از مخرب اتوماتیک داشته باشن؟
و تفکر تعریف کلاس پست شماره 3 این هست که ما با تعریف سازنده و مخرب کلاس به صورت private از کلاس نتونیم خارج از متد فاکتوری create یک شی جدید ایجاد کنیم.
همچنین با استفاده از دستور decltype دیگه نیازی به تعریف مجازی مخرب نخواهیم داشت پس با تعریف کلاس به این شکل نه تنها از سازنده استاتیک با اشاره گر هوشمند استفاده کردیم بلکه با روش decltype هر کلاسی هم که از کلاس ارث بری داشته باشه کار مخرب مجازی را پیاده سازی کردیم.
دستور deltype هم یک type از نوع پارامتر خودش بر میگردونه که در کلاس فوق یک رفرنس از متد ارجاعی را برمیگردونه یه چیزی شبیه به delegate در زبان #c.
چون وقتی از مخرب های هوشمند استفاده میکنیم به نسبت مجازی کردن مخرب کلاس در مصرف حافظه به طرز چشمگیری صرفه جویی کردیم.
به طور مثال در کد زیر که برای سورت وکتور برای یکی از تاپیکها در همین تالار نوشته بودم توجه کنید که چطور از این تکنیک استفاده شده برای بهینه شدن الگوریتم.
struct MyStructs
{
static void destory(MyStructs* my){
delete my ;
}
static unique_ptr<MyStructs , decltype(&destory)> create(int k, string& s){
return unique_ptr<MyStructs , decltype(&destory)>(new MyStructs(k,s) , &destory);
}
int key;
string stringValue;
MyStructs(int k, const string& s) : key(k), stringValue(s) {}
private :
MyStructs() = default;
~MyStructs(){}
};
void customSort(vector<MyStructs*>& vect){
void swapData(const MyStructs* const, MyStructs* const,MyStructs* const);
void swapData2(const MyStructs& , MyStructs* const,MyStructs* const);
for(vector<MyStructs*>::const_iterator ci=vect.cbegin(); ci !=vect.cend(); ++ci){
for(vector<MyStructs*>::const_iterator ji=ci; ji !=vect.cend(); ++ji){
if(vect.size()>0 && (*ji)->stringValue.compare((*ci)->stringValue) < 0){
auto tmp = MyStructs::create((*ci)->stringValue.size(),(*ci)->stringValue);
/* Pass raw pointer to a method can be pointer and value constnt
is thread safe method*/
swapData(tmp.get(),*ci,*ji);
/* Pass a reference to a method can be value constnt but can not pointer constnt */
//swapData2(*tmp.get(),*ci,*ji);
}
}
}
}
void swapData(const MyStructs* const tstruc,MyStructs* const ci,MyStructs* const ji){
ci->stringValue = ji->stringValue;
ji->stringValue = tstruc->stringValue;
ci->key = ji->key;
ji->key = tstruc->key;
}
void swapData2(const MyStructs& tstruc,MyStructs* const ci,MyStructs* const ji){
ci->stringValue = ji->stringValue;
ji->stringValue = tstruc.stringValue;
ci->key = ji->key;
ji->key = tstruc.key;
}