前面讲到过迭代器,其实就是一种智能指针,而一般适用的智能指针包括 std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost:: intrusive_ptr
其中带_ptr是能引用一般变量和类对象,而带_array的是为数组专用
1.什么是智能指针,有啥用
我个人的理解就是一系列的封装好的类,每个智能指针就是这里边某个类的对象,这些类都有一个共同的特点就是对于用户来讲都是能够模拟出自带回收功能和匹配功能指针功能对象的类,属于STL,也就是说属于泛型编程。
可能说得有些绕,那我们先看看为什么需要智能指针,1)首先肯定不是为了书写上的方便,试想我们在c语言中是如何使用指针的,随处都可以定义一个指针,一个复制语句就能复制给指针,的确是比较灵活的,但智能指针就会显得比较繁琐,然而在维护时会比较依赖程序员的自身编程好的习惯,能够重复利用的指针,就不会另外定义占用额外的空间。但是这个如果在特别长的功能函数中就不方便排查了,而智能指针能够根据指针的周期来回收指针,哪怕你申请了过多的指针变量,也不怕出现额外占用空间的问题。2)其次是方便维护,智能指针包含了很多功能函数,例如get(),,reset(),release()增加了代码可读性,3)最重要的是,在对于堆的操作是能够避免没有释放申请的内存或者重复释放内存的情况,导致内存泄露
2.分别介绍
先申明一个类来辅助介绍
class Simple {
public:
Simple(int param = 0) {
number = param;
std::cout << "Simple: " << number << std::endl;
}
~Simple() {
std::cout << "~Simple: " << number << std::endl;
}
void PrintSomething() {
std::cout << "PrintSomething: " << info_extend.c_str() << std::endl;
}
std::string info_extend;
int number;
};
2.1 auto_ptr
属于std名字空间,也属于STL,在头文件memory中,即#include<memory>就可以使用了
此类指针指针常用于单个堆内存,也是用得最多的智能指针
先上一个正确使用的代码:
void TestAutoPtr() {
<span style="white-space:pre"> </span>std::auto_ptr<Simple> my_memory(new Simple(1)); // 创建对象,输出:Simple:1
<span style="white-space:pre"> </span>if (my_memory.get()) { // 判断智能指针是否为空
<span style="white-space:pre"> </span>my_memory->PrintSomething(); // 使用 operator-> 调用智能指针对象中的函数
<span style="white-space:pre"> </span>my_memory.get()->info_extend = "Addition"; // 使用 get() 返回裸指针,然后给内部对象赋值
<span style="white-space:pre"> </span>my_memory->PrintSomething(); // 再次打印,表明上述赋值成功
<span style="white-space:pre"> </span>(*my_memory).info_extend += " other"; // 使用 operator* 返回智能指针内部对象,然后用“.”调用智能指针对象中的函数
<span style="white-space:pre"> </span>my_memory->PrintSomething(); // 再次打印,表明上述赋值成功
<span style="white-space:pre"> </span> }
}
调用
Simple(1)
输出
Simple: 1
PrintSomething:
PrintSomething: Addition
PrintSomething: Addition other
~Simple: 1
PrintSomething:
PrintSomething: Addition
PrintSomething: Addition other
~Simple: 1
下面来看个有问题的用法
void TestAutoPtr2() {
std::auto_ptr<Simple> my_memory(new Simple(1));
if (my_memory.get()) {
std::auto_ptr<Simple> my_memory2; // 创建一个新的 my_memory2 对象
my_memory2 = my_memory; // 复制旧的 my_memory 给 my_memory2
my_memory2->PrintSomething(); // 输出信息,复制成功
my_memory->PrintSomething(); // 崩溃
}
}
最终如上代码导致崩溃,如上代码时绝对符合 C++ 编程思想的,居然崩溃了,跟进 std::auto_ptr 的源码后,我们看到,罪魁祸首是“my_memory2 = my_memory”,这行代码,f赋值运算符被重载了,导致 my_memory 悬空,最后使用时导致崩溃。
赋值运算符重载函数原型:
auto_ptr& operator=(auto_ptr& __a) throw () {
reset(__a.release());
return *this;
}
而其中又设计reset()和release()函数,一并给出源码
void reset(element_type* __p = 0) throw () {
if (__p != _M_ptr) {
delete _M_ptr;
_M_ptr = __p;
}
}
element_type* release() throw () {
element_type* __tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}
其中有些命名大家有可能看不明白我把,类定义源码一部分给出,大家就能看懂了
template<typename _Tp>
class auto_ptr {
private:
_Tp* _M_ptr;//
public:
/// The pointed-to type.
typedef _Tp element_type;
......
};
具体源码,详见:http://www.cnblogs.com/skyofbitbit/p/3649776.html
再看一个使用上有问题的例子
void TestAutoPtr3() {
std::auto_ptr<Simple> my_memory(new Simple(1));
if (my_memory.get()) {
my_memory.release();
}
}
执行结果
Simple: 1
my_memory指向的对象并没有析构
正确的写法应该是这样
void TestAutoPtr3() {
std::auto_ptr<Simple> my_memory(new Simple(1));
if (my_memory.get()) {
Simple* temp_memory = my_memory.release();
delete temp_memory;
}
}
或者
void TestAutoPtr3() {
std::auto_ptr<Simple> my_memory(new Simple(1));
if (my_memory.get()) {
my_memory.reset(); // 释放 my_memory 内部管理的内存
}
}
大家看了源码就知道,release中是没有delete调用的,所以就没有对象的析构函数的调用了,也很好理解
2.2 shared_ptr
boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用
C++最新标准:C++11已将智能指针:shared_ptr、weak_ptr收录为标准库中,即对应为:std::shared_ptr, std::weak_ptr,相应的头文件:<memory>
boost::shared_ptr 也可以很方便的使用。并且没有 release() 函数。关键的一点,boost::shared_ptr 内部维护了一个引用计数,由此可以支持复制、参数传递等。boost::shared_ptr 提供了一个函数 use_count() ,此函数返回 boost::shared_ptr 内部的引用计数。
上代码分析:
void TestSharedPtr(boost::shared_ptr<Simple> memory) { // 注意:无需使用 reference (或 const reference)
memory->PrintSomething();
std::cout << "TestSharedPtr UseCount: " << memory.use_count() << std::endl;
}
void TestSharedPtr2() {
boost::shared_ptr<Simple> my_memory(new Simple(1));
if (my_memory.get()) {
my_memory->PrintSomething();
my_memory.get()->info_extend = "Addition";
my_memory->PrintSomething();
(*my_memory).info_extend += " other";
my_memory->PrintSomething();
}
std::cout << "TestSharedPtr2 UseCount: " << my_memory.use_count() << std::endl;
TestSharedPtr(my_memory);
std::cout << "TestSharedPtr2 UseCount: " << my_memory.use_count() << std::endl;
//my_memory.release();// 编译 error: 同样,shared_ptr 也没有 release 函数
}
执行结果为:
Simple: 1
PrintSomething:
PrintSomething: Addition
PrintSomething: Addition other
TestSharedPtr2 UseCount: 1
PrintSomething: Addition other
TestSharedPtr UseCount: 2
TestSharedPtr2 UseCount: 1
~Simple: 1
2.3 shared_array
boost::shared_array 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用
与shared_ptr 类似,但是用于数组的引用
看例子
void TestSharedArray(boost::shared_array<Simple> memory) { // 注意:无需使用 reference (或 const reference)
std::cout << "TestSharedArray UseCount: " << memory.use_count() << std::endl;
}
void TestSharedArray2() {
boost::shared_array<Simple> my_memory(new Simple[2]);
if (my_memory.get()) {
my_memory[0].PrintSomething();
my_memory.get()[0].info_extend = "Addition 00";
my_memory[0].PrintSomething();
my_memory[1].PrintSomething();
my_memory.get()[1].info_extend = "Addition 11";
my_memory[1].PrintSomething();
//(*my_memory)[0].info_extend += " other"; // 编译 error,scoped_ptr 没有重载 operator*
}
std::cout << "TestSharedArray2 UseCount: " << my_memory.use_count() << std::endl;
TestSharedArray(my_memory);
std::cout << "TestSharedArray2 UseCount: " << my_memory.use_count() << std::endl;
}
执行结果为:
Simple: 0
Simple: 0
PrintSomething:
PrintSomething: Addition 00
PrintSomething:
PrintSomething: Addition 11
TestSharedArray2 UseCount: 1
TestSharedArray UseCount: 2
TestSharedArray2 UseCount: 1
~Simple: 0
~Simple: 0
跟 boost::shared_ptr 一样,使用了引用计数,可以复制,通过参数来传递。