Boost學習系列2-智能指標(下)

來源:互聯網
上載者:User
 3.5、弱指標

    前面的幾種智能指標在不同場合可以獨立使用,然而,弱指標只有在配合共用指標使用時才會有意義,見下面例子:

#include <windows.h> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include <iostream> DWORD WINAPI reset(LPVOID p) {   boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int>*>(p);   sh->reset();   return 0; } DWORD WINAPI print(LPVOID p) {   boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int>*>(p);   boost::shared_ptr<int> sh = w->lock();   if (sh)     std::cout << *sh << std::endl;   return 0; } int main() {   boost::shared_ptr<int> sh(new int(99));   boost::weak_ptr<int> w(sh);   HANDLE threads[2];   threads[0] = CreateThread(0, 0, reset, &sh, 0, 0);   threads[1] = CreateThread(0, 0, print, &w, 0, 0);   WaitForMultipleObjects(2, threads, TRUE, INFINITE); } 

boost::wead_ptr總是通過boost::shared_ptr來初始化的,一旦初始化之後,它基本上只提供一個有用的方法: lock()。此方法返回的boost::shared_ptr與用來初始化弱指標的共用指標共用所有權。 如果這個共用指標不含有任何對象,返回的共用指標也將是空的。

    當函數需要一個由共用指標所管理的對象,而這個對象的生存期又不依賴於這個函數時,就可以使用弱指標。 只要程式中還有一個共用指標掌管著這個對象,函數就可以使用該對象。 如果共用指標複位了,就算函數裡能得到一個共用指標,對象也不存在了。

    第一個線程函數reset的參數是一個共用指標的地址。第二個線程函數print的參數是一個弱指標的地址。這個弱指標是之前通過共用指標初始化的。一旦程式啟動之後,reset和 print就都開始執行了。不過執行順序是不確定的,這就導致了一個潛在的問題:reset線程在銷毀對象的時候print線程可能正在訪問它。這時就可以通過調用弱指標的lock() 函數解決這個問題:如果對象存在,那麼lock()函數返回的共用指標指向這個合法的對象。否則,返回的共用指標被設定為0,這等價於標準的null指標。弱指標本身對於對象的生存期沒有任何影響。lock返回一個共用指標,print函數就可以安全的訪問對象了。這就保證了即使另一個線程要釋放對象,由於我們有返回的共用指標,對象依然存在。

3.6、介入式指標

    其實介入式指標(boost::intrusive_ptr)的工作方式和共用指標幾乎一樣,boost::shared_ptr 在內部記錄著引用到某個對象的共用指標的數量,而對介入式指標,程式員就得自己來做記錄。對於架構對象來說這就特別有用,因為它們記錄著自身被引用的次數。

#include <boost/intrusive_ptr.hpp> #include <atlbase.h> #include <iostream> void intrusive_ptr_add_ref(IDispatch *p) {   p->AddRef(); } void intrusive_ptr_release(IDispatch *p) {   p->Release(); } void check_windows_folder() {   CLSID clsid;   CLSIDFromProgID(CComBSTR("Scripting.FileSystemObject"), &clsid);   void *p;   CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), &p);   boost::intrusive_ptr<IDispatch> disp(static_cast<IDispatch*>(p));   CComDispatchDriver dd(disp.get());   CComVariant arg("C:\\Windows");   CComVariant ret(false);   dd.Invoke1(CComBSTR("FolderExists"), &arg, &ret);   std::cout << (ret.boolVal != 0) << std::endl; } void main() {   CoInitialize(0);   check_windows_folder();   CoUninitialize(); }

上面的例子中使用了COM(元件物件模型)提供的函數,所以只能在Windows平台上編譯運行。COM 物件是使用boost::intrusive_ptr的絕佳範例,因為COM對象需要記錄當前有多少指標引用著它。通過調用AddRef和Release函數,內部的引用計數分別增1或者減1。當引用計數為0時,COM對象自動銷毀。

    在intrusive_ptr_add_ref和intrusive_ptr_release內部調用AddRef和Release這兩個函數,來增加或減少相應COM對象的引用計數。這個例子中用到的COM對象名為 'FileSystemObject',在Windows上它是預設可用的。通過這個對象可以訪問底層的檔案系統,比如檢查一個給定的目錄是否存在。在上例中,我們檢查C:\Windows目錄是否存在。具體它在內部是怎麼實現的,跟boost::intrusive_ptr的功能無關,完全取決於COM。關鍵點在於一旦介入式指標 disp 離開了它的範圍——check_windows_folder() 函數的末尾,函數 intrusive_ptr_release() 將會被自動調用。這將減少COM對象'FileSystemObject'的內部引用計數到0,於是該對象就銷毀了。

3.7、指標容器

    在用C語言時,如果我們要建立一個動態數組,我們可以new一個,但是這樣使用十分麻煩,因此可以選擇更進階寫的容器vector,它可以很好的動態管理數組(push_back、erase等)。對應的,boost中也有這樣的指標:

#include <boost/ptr_container/ptr_vector.hpp> int main() {   boost::ptr_vector<int> v;   v.push_back(new int(1));   v.push_back(new int(2)); }

boost::ptr_vector專門用於動態分配的對象,它使用起來更容易也更高效。 boost::ptr_vector 獨佔它所包含的對象,因而容器之外的共用指標不能共用所有權,這跟 std::vector<boost::shared_ptr<int> > 相反。

除了boost::ptr_vector之外,專門用於管理動態指派至的容器還包括:boost::ptr_deque, boost::ptr_list,boost::ptr_set,boost::ptr_map,boost::ptr_unordered_set和 boost::ptr_unordered_map。這些容器等價於C++標準裡提供的那些。最後兩個容器對應於std::unordered_set和std::unordered_map,它們作為技術報告1的一部分加入C++標準。如果所使用的C++標準實現不支援技術報告1的話,還可以使用Boost C++庫裡實現的 boost::unordered_set和boost::unordered_map。

四、練習題:

最佳化下面兩個程式:

1、

#include <iostream> #include <cstring> char *get(const char *s) {   int size = std::strlen(s);   char *text = new char[size + 1];   std::strncpy(text, s, size + 1);   return text; } void print(char *text) {   std::cout << text << std::endl; } int main(int argc, char *argv[]) {   if (argc < 2)   {     std::cerr << argv[0] << " <data>" << std::endl;     return 1;   }   char *text = get(argv[1]);   print(text);   delete[] text; }

2、

#include <vector> template <typename T> T *create() {   return new T; } int main() {   std::vector<int*> v;   v.push_back(create<int>()); }

五、解答

1、要最佳化這個程式,用範圍數組指標就可以了,它會自動調用delete[]析構

#include <boost/scoped_array.hpp>

char *get(const char *s)

{

       int size = std::strlen(s);

       boost::scoped_array<char> s(new char[size]);

       std::strncpy(text, s, size + 1);

  return text;

}

main函數裡的delete可以不要了,進而避免程式在delete調用之前退出導致的記憶體未釋放。

2、要最佳化它,當然是使用ptr_vector指標了,代碼同上面的例子。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.