Boost学习系列2-智能指针(下)

18 篇文章 0 订阅
12 篇文章 2 订阅
 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指针了,代码同上面的例子。





 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值