參考資料:
因為我目前的跨平台環境(Win10 x64/CentOS7/AIX7)似乎沒有都支援C14(make_unique),所以先測試C11的語法
好好使用unique_ptr可以避免孤兒記憶體(Memory Leak)和繁瑣的delete檢查,但又有些技巧可以轉移指標的所有權(move/shared_ptr),不過我覺得這種作法與"unique"的精神有些矛盾,先不測試和使用
#define STR(N) #N
#define XSTR(N) STR(N)
//__LINE__型態是%d,利用XSTR轉換成%s
#define _LINE_STR_ XSTR(__LINE__)
class MyClass
{
public:
int Value1;
int Value2;
public:
MyClass()
{
Value1 = 10;
Value2 = 20;
printf("Constructor|%s|%s\n", _LINE_STR_, __FUNCTION__); //__FUNCTION__=="MyClass"
}
public:
~MyClass()
{
printf("Destructor|%s|%s\n", _LINE_STR_, __FUNCTION__); //__FUNCTION__=="~MyClass"
}
public:
static MyClass *Factory()
{
return new MyClass();
}
};
int main(const int argc, const char *argv[])
{
{ //unique_ptr練習0
char *buf = new char[PATH_MAX]; //{0};
int cnt = snprintf(buf, PATH_MAX, "%s", __FILE__);
printf("%d|%ld|%ld|%s\n", cnt, sizeof(buf), strlen(buf), buf);
delete[] buf;
//unique_ptr不需要delete,離開作用域{}時,自動釋放資源
std::unique_ptr<char[]> _ptr(new char[PATH_MAX]);
cnt = snprintf(_ptr.get(), PATH_MAX, "%s", __FILE__);
printf("%d|%ld|%ld|%s\n\n", cnt, sizeof(_ptr.get()), strlen(_ptr.get()), _ptr.get());
}
{ //Lambda & unique_ptr練習1
auto lambda = [](void) { return new MyClass(); };
std::unique_ptr<MyClass> _uniPtr(lambda());
//[&](void) { ... }(),建立匿名方法(Anonymous Method),並立刻執行
//unique_ptr不能複製,因為匿名方法有傳遞unique_ptr,
//所以只能用[&]傳址傳遞參數,用[=]傳值傳遞參數編譯會跳錯
[&](void) { printf("unique_ptr test 1|%d|%s|%s\n", _uniPtr.get()->Value1, _LINE_STR_, __FUNCTION__); }();
} //不需要delete,unique_ptr離開作用域{}時,自動會呼叫解構子~MyClass()
{ //Lambda & unique_ptr練習2
std::function<MyClass *()> func = MyClass::Factory;
std::unique_ptr<MyClass> _uniPtr(func());
[&](void) { printf("unique_ptr test 2|%d|%s|%s\n", _uniPtr.get()->Value2, _LINE_STR_, __FUNCTION__); }();
}
{ //Lambda & unique_ptr練習3
try
{
std::unique_ptr<MyClass> _uniPtr1(new MyClass());
[&](void) { printf("_uniPtr1|%d|%s|%s\n", _uniPtr.get()->Value1, _LINE_STR_, __FUNCTION__); }();
std::unique_ptr<MyClass> _uniPtr2(new MyClass());
[&](void) { printf("_uniPtr2|%d|%s|%s\n", _uniPtr.get()->Value2, _LINE_STR_, __FUNCTION__); }();
throw std::invalid_argument("unique_ptr test 3");
}
//unique_ptr在作用域{}內發生exception,也仍然會觸發解構子~MyClass(),
//所以資源仍然會被釋放,不會造成孤兒記憶體(Memory Leak)
catch (const std::invalid_argument& e)
{
printf("Error test|std::invalid_argument|%s|%s|%s\n", e.what(), _LINE_STR_, __FUNCTION__);
}
//解構子和catch的設計要非常小心,內部如果又發生其他錯誤,程式還是會當掉,是很常見的大災難
}
return EXIT_SUCCESS;
}
觀察執行時間,利用unique_ptr管理資源,比自行設計new/delete,要多大約1 usec的時間,但可以簡化很多行程式碼
unique_ptr應該要放在生命週期較長的作用域{}內,再傳址(&)給其他生命週期較短的方法使用
以下範例紀錄一般指標_ptr的生命週期大於unique_ptr,程式沒當掉,但會得到錯誤的值,難以解釋,宜避免
MyClass *_ptr = NULL;
{
_ptr = new MyClass();
std::unique_ptr<MyClass> _uniPtr(_ptr);
} //unique_ptr離開作用域{},觸發解構子~MyClass()
//_ptr->Value1不是10,每次測試都是不同的亂數,不會引發錯誤,難以解釋
printf("unique_ptr test 4|%d|%s|%s\n", _ptr->Value1, _LINE_STR_, __FUNCTION__);
_ptr = NULL;