有时,我们可能需要一个包含指针的容器.比如存放一些不可拷贝的对象或者想在容器里存放基类以实现多态.尽管我们可以直接定义存放指针的STL容器,不过这样并不方便,我们得处处小心,在清空或删除容器里的元素时我们要保证异常安全地回收指针所指向的对象,以防内存泄漏.
一个好的办法是使用共享智能指针的容器,如std::vector<boost::shared_ptr<T> >,不过这样做的话智能指针所带来的开销显然太大了.
Boost.pointer_container提供了和标准相似的容器,只是它维护的是指针所指的对象.
要使用Boost.pointer_container只需包含下面的头文件即可
#include <boost/ptr_container/ptr_container.hpp>
一个简单的例子
- #include <iostream>
- #include <boost/ptr_container/ptr_container.hpp>
- using namespace std;
- using namespace boost;
- struct base{ //基类
- virtual void print()=0;
- virtual ~base(){}
- };
- struct A:base{ //A,从base继承
- void print(){
- cout << "A" << endl;
- }
- };
- struct B:base{ //B,从base继承
- void print(){
- cout << "Here is B" << endl;
- }
- };
- struct C:base{ //C,从base继承
- C(string str)
- :m_str(str){}
- void print(){
- cout << "C:" << m_str << endl;
- }
- private:
- string m_str;
- };
- int main()
- {
- ptr_vector<base> data;
- data.push_back(new A);
- data.push_back(new B);
- data.push_back(new C("hello"));
- data.push_back(new C("world"));
- for(ptr_vector<base>::iterator itr=data.begin(); itr!=data.end(); ++itr)
- {
- itr->print();
- }
- return 0;
- }
运行结果:
A Here is B C:hello C:world
应该注意到,这里没有内存泄漏,在ptr_vector<base>析构时会删除容器内指针所指向的对象.
本例中,我们使用了ptr_vector,在Boost.pointer_container库中包含的指针容器有:
ptr_vector ptr_list ptr_deque ptr_array ptr_set ptr_multi_set ptr_map ptr_multimap
除ptr_array以外,其它所有的容器都能与STL一一对应起来,除了以指针方式存入以外,使用方法没什么区别.至于ptr_array,它不过是Boost.Array的简单包装罢了.
毫无悬念地,这些指针容器是STL兼容的,比如上面的循环打印用这句代替:
for_each(data.begin(), data.end(), mem_fun_ref(&base::print));
其它问题
空值
如果向容器中插入NULL, 默认情况是抛出一个异常. 如果想让指针容器能保存NULL指针,那么声明容器时就必须使用nullable来包装数据类型,例如:
boost::ptr_vector< boost::nullable<base> > animals_type;
克隆能力
默认情况下,容器或迭代器之间的赋值并不是简单的指针复制,而是使用拷贝构造函数产生一个新的拷贝.比如:
- struct C:base{
- C(string str)
- :m_str(str){
- cout << "C:con " << this << ' ' << str <<endl;
- }
- C(){
- cout << "C:con " << this <<endl;
- }
- C(const C& oth){
- m_str = oth.m_str;
- cout << "C:copy " << this << ' ' << m_str <<endl;
- }
- ~C(){
- cout << "C:decon " << this <<endl;
- }
- void print(){
- cout << "C:" << m_str << endl;
- }
- private:
- string m_str;
- };
- int main()
- {
- ptr_vector<C> data;
- data.push_back(new C("hello"));
- data.push_back(new C("world"));
- ptr_vector<C> dataX = data;
- return 0;
- }
显示两次构造,两次拷贝,四次析构. 说明ptr_vector<C> dataX = data;使用了拷贝构造函数生成了自己的对象实例.
我们可以通过定义T* new_clone(const T&)来改变这种行为:
在上例的main()函数之前定义:
- C* new_clone(const C& orig)
- {
- cout << "C:clone" << endl;
- return new C("cloned");
- }
这次运行结果显示它调用了两次new_clone而不是拷贝了.