boost智能指针
文章目录
- 1.0: RAII
- 1.1作用域指针
- 1.2作用域数组
- 1.3 共享指针
- 1.4共享数组
- 1.5弱指针
- 1.6指针容器
1.0: RAII
一种思想
智能指针的原理基于一个常见的习语叫做 RAII :资源申请即初始化。 智能指针只是这个习语的其中一例——当然是相当重要的一例。 智能指针确保在任何情况下,动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放了出来。 这包括程序因为异常而中断,原本用于释放内存的代码被跳过的场景。 用一个动态分配的对象的地址来初始化智能指针,在析构的时候释放内存,就确保了这一点。 因为析构函数总是会被执行的,这样所包含的内存也将总是会被释放。
无论何时,一定得有第二条指令来释放之前另一条指令所分配的资源时,RAII 都是适用的。 许多的 C++ 应用程序都需要动态管理内存,因而智能指针是一种很重要的 RAII 类型。 不过 RAII 本身是适用于许多其它场景的。
1.1作用域指针
一个作用域指针独占一个动态分配的对象。 对应的类名为 boost::scoped_ptr,它的定义在 boost/scoped_ptr.hpp 中。 不像 std::auto_ptr,一个作用域指针不能传递它所包含的对象的所有权到另一个作用域指针。 一旦用一个地址来初始化,这个动态分配的对象将在析构阶段释放。
因为一个作用域指针只是简单保存和独占一个内存地址,所以 boost::scoped_ptr 的实现就要比 std::auto_ptr 简单。 在不需要所有权传递的时候应该优先使用 boost::scoped_ptr 。 在这些情况下,比起 std::auto_ptr 它是一个更好的选择,因为可以避免不经意间的所有权传递。
特点:独占 无法被传递
//作用域指针 当你的对象保证不会被传递到其他作用域的时候 可以使用这个指针来进行限制
#include <boost/scoped_ptr.hpp> #include <iostream>
int main()
{boost::scoped_ptr<int> i(new int);*i = 1;*i.get() = 2;std::cout << *i << std::endl;i.reset(new int);
}
一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。 这是因为重载了相关的操作符 operator*(),operator->() 和 operator bool() 。 此外,还有 get() 和 reset() 方法。 前者返回所含对象的地址,后者用一个新的对象来重新初始化智能指针。 在这种情况下,新创建的对象赋值之前会先自动释放所包含的对象。
1.2作用域数组
特点:无法传递 只能在作用域内访问 通过[]访问
#include <boost/scoped_array.hpp> int main()
{boost::scoped_array<int> i(new int[2]);*i.get() = 1;i[1] = 2;i.reset(new int[3]);
}
1.3 共享指针
特点:通过boost::shared_ptr创建的智能指针可以被移动到容器内部 如果是上面的作用域指针就会报错
现在C++自己也有共享指针了 可以使用 memory 中定义的 std::shared_ptr
共享指针的特点是 最后一个引用对象的指针被销毁掉了 这个对象就会自动调用析构函数
代码
#include <boost/shared_ptr.hpp>
#include <vector> int main()
{std::vector<boost::shared_ptr<int> > v;v.push_back(boost::shared_ptr<int>(new int(1)));v.push_back(boost::shared_ptr<int>(new int(2)));
}
1.4共享数组
共享数组的行为类似于共享指针。 关键不同在于共享数组在析构时,默认使用 delete[] 操作符来释放所含的对象。 因为这个操作符只能用于数组对象,共享数组必须通过动态分配的数组的地址来初始化。
#include <boost/shared_array.hpp>
#include <iostream> int main()
{boost::shared_array<int> i1(new int[2]);boost::shared_array<int> i2(i1);i1[0] = 1;std::cout << i2[0] << std::endl;
}
1.5弱指针
弱指针 只能配合共享指针一起使用
#include <Windows.h>
#include <iostream> #include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp> 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);
}
代码的原理是 调用window的线程 一个线程进行重置 一个线程进行输出 普通情况下 如果在打印的时候 重置了这个对象 就会导致访问失败 ,但是我们这里使用了w->lock();
可以看到lock实际上就是再创建一个共享指针 让这个指针的引用不会到0 不会调用析构函数
通过调用弱指针的 lock() 函数可以解决这个问题:如果对象存在,那么 lock() 函数返回的共享指针指向这个合法的对象。否则,返回的共享指针被设置为0,这等价于标准的null指针。
弱指针本身对于对象的生存期没有任何影响。 lock() 返回一个共享指针,print() 函数就可以安全的访问对象了。 这就保证了——即使另一个线程要释放对象——由于我们有返回的共享指针,对象依然存在。
1.6指针容器
#include <boost/shared_ptr.hpp>
#include <vector> int main()
{ std::vector<boost::shared_ptr<int> > v; v.push_back(boost::shared_ptr<int>(new int(1))); v.push_back(boost::shared_ptr<int>(new int(2)));
}
上面代码的缺陷
第一,反复声明 boost::shared_ptr 需要更多的输入。 其次,将 boost::shared_ptr 拷进,拷出,或者在容器内部做拷贝,需要频繁的增加或者减少内部引用计数,这肯定效率不高。 由于这些原因,Boost C++ 库提供了 指针容器 专门用来管理动态分配的对象。
使用下面的指针容器
#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_container/ptr_vector.hpp 里,它跟前一个例子中用 boost::shared_ptr 模板参数来初始化的容器具有相同的工作方式。 boost::ptr_vector 专门用于动态分配的对象,它使用起来更容易也更高效。