简介及基本用法(3)

前面的几种智能指针在不同场合可以独立使用,然而,弱指针只有在配合共享指针使用时才会有意义,见下面例子:

#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指针了,代码同上面的例子。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwjfxg.html