0%

Some notes about unique_ptr

以往在C++98寫的Code,䅰習慣用Raw Pointer來操作Object,在C++11/14之後,有了更安全的面貌。少了new/delete之後,接觸到Memory Leak的機會變少了。

Insert Element into container

1
2
3
deque<Object *> objs;
for (int i = 0; i < 3; i++)
objs.push_back(new Object());

現在可以這麼寫

1
2
3
deque<unique_ptr<Object>> objs;
for (int i = 0; i < 3; i++)
objs.push_back(make_unique<Object>());

C++11有make_shared部過沒有make_unique,到C++14才加入,部過我們可以打造個一模一樣的版本。

1
2
3
4
5
6
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args ) {
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
for (int i = 0; i < 3; i++)
objs.push_back(make_unique<Object>(i));

Erase element from Container

以往的寫法大概就像這樣

1
2
3
4
5
6
deque<Object*>::iterator  it = std::find_if(objs.begin(), objs.end(), [](const Object *obj) -> bool { ... });

if (it != objs.end()) {
delete *it;
objs.erase(it);
}

最常望記得就是那個delete,然後就造成Memory leak。
新的寫法就像這樣

1
2
3
4
auto it = std::find_if(objs.begin(), objs.end(), [](const unique_ptr<Object> &obj) -> bool { ...});

if (it != objs.end())
objs.erase(it);

被指向的Object resource會在某個時間點被歸還。

Get element from container

這邊可分成兩部份,單純瀏覽整個Container中的Elemeent,不對Container作任何動作。
或是當作資料結構,會改變Container本身的狀態。
先從第一種來說明,以往可能這麼作

1
2
3
4
5
for (auto it = objs.begin(); it != objs.end(); ++it)
{
Object *obj = *it;
// Do something
}

用了unique_ptr之後,只能這麼做了

1
2
3
4
5
for (auto it = objs.begin(); it != objs.end(); ++it)
{
Object *obj = (*it).get();
// Do something
}

如果要直接操作Container,像輕空Buffer queue的情形時,以前的作法

1
2
3
4
5
6
7
8
while (!objs.empty())
{
cout << "Move from vector" << endl;
Object *pObj = objs.front();
objs.pop_front();
// Do something
delete pObj;
}

不過由於unique_ptr不像auto_ptr擁有copy semantics,因此上面的程式碼要改成

1
2
3
4
5
6
7
while (!objs.empty())
{
cout << "Move from vector" << endl;
unique_ptr<Object> s = std::move(objs.front());
objs.pop_front();
// Do something
}

可以看到,跟Raw Pointer操作相差無幾,不過利用RAII技術減少Memory leak的發生。