mencoba smart pointer : pinter pointer dari boost

Smart pointer adalah pointer yang smart. :d. ya memang begitu tujuannya. Seperti diketahui tidak ada management object secara otomatis [garbage collecor] di c/c++, jadi setiap dynamic-object yang di alokasi di heap dan sudah waktunya dihapus harus di-delete secara eksplisit. (dynamic-object == setiap object yang di-create dengan keyword new). Apa akibatnya jika delete tidak dilakukan?, kemungkinan terjadi memory leak ( : keadaan dimana pembuat program tidak bahagia). kemungkinan memory leak ini semakin besar seiring semakin kompleknya project, dan juga memory leak akan susah dicari :| . untuk itu dibuat smart pointer: pointer yang mengurusi kepemilikan object (secara singkatnya begitu) . Smart pointer sudah lama digunakan, namun tetap saja masih banyak project yang menggunakan raw pointer , (dengan alasan kemudahan dan malas. haha.) . Kelemahan dari smart pointer , tentu akan lebih lambat dari raw pointer, karena smart pointer sebenarnya adalah wrapper class untuk pointer. Walaupun ada, saya yakin, keuntungannya lebih tinggi karena kecepatan processor yang semakin tinggi dan project yang rumit. {riil project == pasti rumit }. Okey, itulah motivasinya :), saatnya mencoba smart pointer. Saya pilih smart pointer dari Boost library yang banyak digunakan. Di Boost terdapat beberapa smart pointer, saya akan mencoba 3 diantaranya: scoped pointer, shared pointer, dan weak pointer.

1. scoped pointer.

Di dalam fungsi (atau apapun yang ada di scope “{“ sampai “}” ) , object yang ada di stack otomatis di free kalau out of scope tapi kalau pointer tidak. Tujuan scoped_pointer adalah membuat pointer otomatis di free kalau out of scope , apapun yang terjadi. Misalnya saya punya sebuah kelas:

[sourcecode language=“cpp”] class bigObject { private: int m_obj_id; public: bigObject(int id) { m_obj_id = id; std::cout<<” constructor call .object id: “<<m_obj_id<<std::endl; }; ~bigObject() { std::cout<<” destructor call .object id: “<<m_obj_id<<std::endl; }; void doSomething() { std::cout<<” bigObject is doing something .object id: “<<m_obj_id<<std::endl; }; }; [/sourcecode]

lalu ada fungsi yang menggunakan seperti dibawah ini:

[sourcecode language=“cpp”] void complexFunc() { bigObject* object01 = new bigObject(1); bigObject* object02 = new bigObject(2); bigObject* object03; boost::scoped_ptr object04 ( new bigObject(4) ) ;

// function do complex stuff here // .. // .. bool complex_condition =false; if(complex_condition) { std::cout<<” return early “<<std::endl; return; // #possibility 1 } try { bigObject* object03 = new bigObject(2); // doing other complex-advanced-algotithm here // .. // .. throw 13; // but an exception is trown here // #possibility 2 //..

  delete object03;

} catch (int e) { std::cout<<” exception happen! your lucky number is: “<<e<<std::endl; } // delete object01; forget to call #possibility 3 delete object02; } [/sourcecode]

Di fungsi void complexFunc() diatas, terdapat instansiasi class bigObject, masing-masing : object01, object02, object03 dan object04. Object04 adalah scoped_tr dan yang lain adalah raw pointer. Terdapat 3 kemungkinan object tidak di delete di fungsi di atas.

#possibility 1 : dalam kondisi tertentu, variable boolean complex_condition menjadi true sehingga fungsi return. Akibatnya delete object02; tidak dipanggil. object04 ? .aman, di delete otomatis.

#possibility 2 : terjadi exception , misalnya saja exception 13 , sehingga delete object03; tidak dipanggil. object04 ? .aman, di delete otomatis.

#possibility 3 : object01 lupa tidak di delete (tukang ketik juga manusia) . object04 ? .aman, di delete otomatis.

 Jadi, penggunaan scoped_ptr sangat menguntungkan. Hehehe. #devil_laugh

2. shared pointer.

Seperti namanya , pointer ini digunakan untuk object yang ditunjuk oleh beberapa pointer. Tujuan penggunaan nya adalah men-track jumlah pointer yang menunjuk sebuah object di heap. Jika jumlah penunjuk object =0, object akan di delete otomatis. Salah satu contoh riil penggunaan nya adalah saat menyimpan pointer ke dalam vector.

[sourcecode language=“cpp”] std::vector vec; std::vector< boost::shared_ptr > vec2;

for(int i=0;i<10;i++) { // fill vector vec bigObject* temp =new bigObject(i); vec.push_back(temp); // fill vector vec2 boost::shared_ptr temp2 (new bigObject(i+10)); vec2.push_back(temp2); } vec.erase(vec.begin() + 0); vec2.erase(vec2.begin() + 0);

vec.clear(); vec2.clear(); [/sourcecode]

Code diatas vec dan vec2 adalah vector dari pointer ke bigObject, bedanya vec menggunakan raw pointer sedangkan vec2 menggunakan shared pointer. Setelah kode keluar for {.. } vec dan vec2 terisi, dan yang memiliki pointer ke bigObject adalah vector itu sendiri. Jadi jumlah pointer ke bigObject masing-masing =1 untuk setiap object didalam vector.

vec.erase(vec.begin() + 0); : ref counted menjadi 0 untuk object index 0 di vector, object ke 0 masih ada di heap dan tidak pointer yang menunjuk. ( kandidat memory leak )

vec2.erase(vec2.begin() + 0); : ref counted menjadi 0 untuk object index 0 di vector, object ke 0 otomastis di di delete . sama halnya terjadi saat pemanggilan clear(). Jadi saat men-delete anggota vec , delete harus dilakukan manual. Clear yang benar untuk vec :

for(std::vector::iterator it=vec.begin(); it!=vec.end(); it++) {

      delete (*it); } vec.clear();

erase dan clear vector memanggil destructor object, karena yang di hold disini adalah pointer ke object , maka destructor tidak dipanggil.

 3. weak pointer.

Akan ada masalah saat penggunaan shared_ptr jika terdapat keadaan dimana terdapat 2 atau lebih pointer yang saling menunjuk, yang mengakibatkan jumlah counter objek di shared_ptr tidak akan pernah 0, yang artinya objek tidak akan pernah di delete== tujuan shared_ptr gagal. Contoh simple situasi ini, sbb:

[sourcecode language=“cpp”] class kelasB;

class kelasA { public: kelasA() { std::cout<<” kelasA constructor call “< _pB) { m_B=_pB; } ~kelasA() { std::cout<<” kelasA destructor call “< m_B; };

class kelasB { public: kelasB() { std::cout<<” kelasB constructor call “< _pA) { m_A=_pA; m_A. ->doing(); } ~kelasB() { std::cout<<” kelasB destructor call “< m_A; };

void testFunc() { boost::shared_ptr aaa(new kelasA()); boost::shared_ptr bbb(new kelasB());

aaa->setB(bbb); bbb->setA(aaa); } [/sourcecode]

Di testFunc() , seharusnya aaa dan bbb di delete otomatis , tetapi itu tidak terjadi karena aaa dan bbb saling menunjuk yang menyebabkan jumlah objek di shared_ptr tidak pernah 0. untuk mengatasi ini, digunakan weak_ptr. Dalam kasus diatas dengan mengganti m_A di kelas A dan m_B dikelas B, menjadi boost::weak_ptr m_A; dan boost::weak_ptr m_B; . weak_ptr tidak memiliki overload operator →, jadi aaa->doSomething(); akan compile error. Jadi bagaimana ?. Untuk mengakses m_A atau m_B harus digunakan lock() .

       m_A.lock() ->doSomething();

       m_B.lock() ->doSomething();

 .lock() akan membuat local copy dari object yang ditunjuk oleh pointer. Jadi misalnya object yang ditunjuk sudah di delete tidak akan masalah. Misalnya:

 if(kelasB* temp= m_B.lock()) {            //doing other thing here           //..           //..            temp->DoSomething(); }

Dimungkinkan sebelum temp->DoSomething() dipanggil, m_B sudah di delete atau m_B.reset() dipanggil. Namun disini, temp->DoSomething() tidak akan crash.

Posting ini adalah tutorial awal pengenalan smart pointer. untuk memahaminya lebih dalam tentu harus menggunakanya secara intensif di setiap project. semua code diatas dites di visual c++ 2008. Jika ada kesalahan, silahkan komen. 

? : saat copy paste source code dari open office / notepad++ ke worpress editor identation nya jadi hilang. :|

< edi dot ermawan at gmail dot com > . yogyakarta, 01/07/2012 .

comments powered by Disqus