C++中智能指针的工作原理

智能指针:它的一种通用实现方法是采用引用计数的方法。智能指针将一个计数器与类指向的对象相关联,引用计数跟踪共有多少个类对象共享同一指针。

每次创建类的新对象时,初始化指针并将引用计数置为1;
当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;这是因此左侧的指针指向了右侧指针所指向的对象,因此右指针所指向的对象的引用计数+1;
调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。

STL中的auto_ptr

STL中智能指针 auto_ptr(C++98)所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。

但是auto_ptr有缺陷,在使用时要注意避免。

不要将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果。

1、auto_ptr不能共享所有权。
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员。
4、不能通过赋值操作来初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OK
std::auto_ptr<int> p = new int(42); //ERROR
这是因为auto_ptr 的构造函数被定义为了explicit
5、不要把auto_ptr放入容器

因为这些缺陷,所以在新版c++11标准中被废弃了(deprecated),c++11引进了boost中shared_ptr  和weak_ptr,增加了 unique_ptr (类似boost中scoped_ptr,但unique_ptr可以转移所有权).
unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:

1、拥有它所指向的对象。

2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是可以进行移动构造和移动赋值操作。

3、保存指向某个对象的指针,当它本身被删除释放的时候(比如,离开了某个作用域),会使用给定的删除器释放它指向的对象。

使用unique_ptr,可以实现以下功能,包括:

1、为动态申请的内存提供异常安全。

2、将动态申请内存的所有权传递给某个函数。

3、从某个函数返回动态申请内存的所有权。

4、在容器中保存指针。

5、所有auto_ptr应该具有的(但无法在C++ 03中实现的)功能。

下面是一段传统的会产生不安全异常的代码:

X* f()
{
X* p = new X;
// 做一些事情,可能会抛出某个异常
return p;
}

解决方法是,使用unique_ptr来管理这个对象的所有权,由其进行这个对象的释放工作。
X* f()
{
unique_ptr<X> p(new X);
// 做一些事情,可能会抛出异常
return p.release();
}

如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象。但是,除非我们真的需要返回一个内建的指针,我们还可以返回一个unique_ptr。

unique_ptr<X> f()
{
unique_ptr<X> p(new X);
// 做一些事情,可能会抛出异常
return p;
}

现在,我们可以这样使用函数f():

void g()
{
unique_ptr<X> q = f(); // 使用移动构造函数(move constructor)
q->DoSomething(); // 使用q
X x = *q; // 复制指针q所指向的对象
} // 在函数退出的时候,q以及它所指向的对象都被删除释放

unique_ptr具有移动语义(move semantics),所以我们可以使用函数f()返回的右值对q进行初始化,这样就简单地将所有权传递给了q。

c++11中智能指针使用:

Using C++11’s Smart Pointers 

 

Boost 中的智能指针

以下主要介绍 boost中的智能指针的使用。

内存管理是一个比较繁琐的问题,C++中有两个实现方案: 垃圾回收机制和智能指针。垃圾回收机制因为性能等原因不被C++的大佬们推崇, 而智能指针被认为是解决C++内存问题的最优方案。

1. 定义

一个智能指针就是一个C++的对象, 这对象的行为像一个指针,但是它却可以在其不需要的时候自动删除。注意这个“其不需要的时候”, 这可不是一个精确的定义。这个不需要的时候可以指好多方面:局部变量退出函数作用域、类的对象被析构……。所以boost定义了多个不同的智能指针来管理不同的场景。

shared_ptr<T> 内部维护一个引用计数器来判断此指针是不是需要被释放。是boost中最常用的智能指针了。
scoped_ptr<t> 当这个指针的作用域消失之后自动释放
intrusive_ptr<T> 也维护一个引用计数器,比shared_ptr有更好的性能。但是要求T自己提供这个计数器。
weak_ptr<T> 弱指针,要和shared_ptr 结合使用
shared_array<T> 和shared_ptr相似,但是访问的是数组
scoped_array<T> 和scoped_ptr相似,但是访问的是数组

2. Boost::scoped_ptr<T>

scoped_ptr 是boost中最简单的智能指针。scoped_ptr的目的也是很简单, 当一个指针离开其作用域时候,释放相关资源。特别注意的一定就是scoped_ptr 不能共享指针的所有权也不能转移所有权。也就是说这个内存地址就只能给的声明的变量用,不能给其他使用。

下面是scoped_ptr的几个特点:

  1. scoped_ptr的效率和空间的消耗内置的指针差不多。
  2. scoped_ptr不能用在标准库的容器上。(用shared_ptr代替)
  3. scoped_ptr 不能指向一块能够动态增长的内存区域(用scoped_array代替)
 1 class test 
 2 { 
 3 public: 
 4     void print() 
 5     { 
 6         cout << "test print now" <<endl; 
 7     } 
 8 };
 9 int _tmain(int argc, _TCHAR* argv[]) 
10 { 
11     boost::scoped_ptr<test> x(new test); 
12     x->print(); 
13     return 0; 
14 }

 

3.Boost::shared_ptr<T>

shared_ptr 具有如下几个特点:

  1. 在内部维护一个引用计数器, 当有一个指针指向这块内存区域是引用计数+1, 反之-1, 如果没有任何指针指向这块区域, 引用计数器为0,释放内存区域。
  2. 可以共享和转移所有权。
  3. 可以被标准库的容器所使用
  4. 不能指向一块动态增长的内存(用share_array代替)

我们可以看下如下例子:

 

1 int _tmain(int argc, _TCHAR* argv[]) 
2 { 
3     boost::shared_ptr<test> ptr_1(new test); 
4     ptr_1->print();//引用计数为1
5     boost::shared_ptr<test> ptr_2 = ptr_1; 
6     ptr_2->print();//引用计数为2
7     ptr_1->print();// 引用计数还是为2
8     return 0; 
9 }

 

4. Boost::intrusive_ptr<T>

intrusive_ptr 的主要和share_ptr一样, 对比share_ptr,其效率更高,但是需要自己维护一个引用计数器。

Intrusive_ptr——轻量级共享智能指针

shared_ptr比普通指针提供了更完善的功能。有一个小小的代价,那就是一个共享指针比普通指针占用更多的空间,每一个对象都有一个共享指针,这个指针有引用计数器以便于释放。但对于大多数实际情况,这些都是可以忽略不计的。

intrusive_ptr 提供了一个折中的解决方案。它提供了一个轻量级的引用计数器,但必须对象本身已经有了一个对象引用计数器。这并不是坏的想法,当你自己的设计的类中实现智能指针相同的工作,那么一定已经定义了一个引用计数器,这样只需要更少的内存,而且可以提高执行性能。

如果你要使用intrusive_ptr 指向类型T,那么你就需要定义两个函数:intrusive_ptr_add_ref 和intrusive_ptr_release。下面是一个简单的例子解释如何在自己的类中实现:

#include "boost/intrusive_ptr.hpp"// forward declarationsclass CRefCounted;namespace boost

{

void intrusive_ptr_add_ref(CRefCounted * p);

void intrusive_ptr_release(CRefCounted * p);

};

 

// My Class

class CRefCounted

{

private:

long references;

friend void ::boost::intrusive_ptr_add_ref(CRefCounted * p);

friend void ::boost::intrusive_ptr_release(CRefCounted * p);

 

public:

CRefCounted() : references(0) {} // initialize references to 0

};

 

// class specific addref/release implementation

// the two function overloads must be in the boost namespace on most compilers:

namespace boost

{

inline void intrusive_ptr_add_ref(CRefCounted * p)

{

// increment reference count of object *p

++(p->references);

}

 

 

 

inline void intrusive_ptr_release(CRefCounted * p)

{

// decrement reference count, and delete object when reference count reaches 0

if (--(p->references) == 0)

delete p;

}

} // namespace boost

 

这是一个最简单的(非线程安全)实现操作。但作为一种通用的操作,如果提供一种基类来完成这种操作或许很有使用价值,也许在其他地方会介绍到。

5. Boost::weak_ptr<T>

weak_ptr 就是一个弱指针。weak_ptr 被shared_ptr控制, 它可以通过share_ptr的构造函数或者lock成员函数转化为share_ptr。

weak_ptr的一个最大特点就是它共享一个share_ptr的内存,但是无论是构造还是析构一个weak_ptr 都不会影响引用计数器。

 1 int _tmain(int argc, _TCHAR* argv[]) 
 2 { 
 3     boost::shared_ptr<test> sharePtr(new test);;
 4     boost::weak_ptr<test> weakPtr(sharePtr); 
 5     //weakPtr 就是用來保存指向這塊內存區域的指針的 
 6     //干了一大堆其他事情
 7     boost::shared_ptr<test> sharePtr_2 = weakPtr.lock(); 
 8     if (sharePtr_2) 
 9         sharePtr_2->print();
10     return 0; 
11 }

6. Boost::shared_array<T> 和Boost::scoped_array<T>

前面提到过shared_ptr和scoped_ptr不能用于数组的内存(new []),所以shared_array和scoped_array就是他们的代替品。我们可以看下shared_array的用法

1 int _tmain(int argc, _TCHAR* argv[]) 
2 { 
3     const int size = 10; 
4     boost::shared_array<test> a(new test[]);
5     for (int i = 0; i < size; ++i) 
6         a[i].print();
7     return 0; 
8 }

7. 使用智能指针的几个注意点

下面是几个使用智能指针需要注意的地方:

  1. 声明一个智能指针的时候要立即给它实例化, 而且一定不能手动释放它。
  2. …_ptr<T> 不是T* 类型。所以:

a: 声明的时候要…_ptr<T> 而不是….._ptr<T*>

b:不能把T* 型的指针赋值给它

c: 不能写ptr=NULl, 而用ptr.reset()代替。

  1. 不能循环引用。
  2. 不要声明临时的share_ptr, 然后把这个指针传递给一个函数

====================================

一、简介

由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete。程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见。

用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法。包括:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost:: intrusive_ptr。你可能会想,如此多的智能指针就为了解决new、delete匹配问题,真的有必要吗?看完这篇文章后,我想你心里自然会有答案。

下面就按照顺序讲解如上 7 种智能指针(smart_ptr)。

 

二、具体使用

1、总括

对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。

访问智能指针包含的裸指针则可以用 get() 函数。由于智能指针是一个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要这样判断:if (my_smart_object.get())。

智能指针包含了 reset() 方法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。

我们编写一个测试类来辅助分析:

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、std::auto_ptr

std::auto_ptr 属于 STL,当然在 namespace std 中,包含头文件 #include<memory> 便可以使用。std::auto_ptr 能够方便的管理单个堆内存对象。

我们从代码开始分析:

void TestAutoPtr() {

std::auto_ptr<Simple> my_memory(new Simple(1));   // 创建对象,输出:Simple:1

if (my_memory.get()) {                            // 判断智能指针是否为空

my_memory->PrintSomething();                    // 使用 operator-> 调用智能指针对象中的函数

my_memory.get()->info_extend = "Addition";      // 使用 get() 返回裸指针,然后给内部对象赋值

my_memory->PrintSomething();                    // 再次打印,表明上述赋值成功

(*my_memory).info_extend += " other";           // 使用 operator* 返回智能指针内部对象,然后用“.”调用智能指针对象中的函数

my_memory->PrintSomething();                    // 再次打印,表明上述赋值成功

}

}                                                   // my_memory 栈对象即将结束生命期,析构堆对象 Simple(1)

执行结果为:

Simple: 1

PrintSomething:

PrintSomething: Addition

PrintSomething: Addition other

~Simple: 1

上述为正常使用 std::auto_ptr 的代码,一切似乎都良好,无论如何不用我们显示使用该死的 delete 了。

 

其实好景不长,我们看看如下的另一个例子:

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”,这行代码,my_memory2 完全夺取了 my_memory 的内存管理所有权,导致 my_memory 悬空,最后使用时导致崩溃。

所以,使用 std::auto_ptr 时,绝对不能使用“operator=”操作符。作为一个库,不允许用户使用,确没有明确拒绝[1],多少会觉得有点出乎预料。

 

看完 std::auto_ptr 好景不长的第一个例子后,让我们再来看一个:

void TestAutoPtr3() {

std::auto_ptr<Simple> my_memory(new Simple(1));

 

if (my_memory.get()) {

my_memory.release();

}

}

执行结果为:

Simple: 1

看到什么异常了吗?我们创建出来的对象没有被析构,没有输出“~Simple: 1”,导致内存泄露。当我们不想让 my_memory 继续生存下去,我们调用 release() 函数释放内存,结果却导致内存泄露(在内存受限系统中,如果my_memory占用太多内存,我们会考虑在使用完成后,立刻归还,而不是等到 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 内部管理的内存

}

}

原来 std::auto_ptr 的 release() 函数只是让出内存所有权,这显然也不符合 C++ 编程思想。

总结:std::auto_ptr 可用来管理单个对象的对内存,但是,请注意如下几点:

(1)    尽量不要使用“operator=”。如果使用了,请不要再使用先前对象。

(2)    记住 release() 函数不会释放对象,仅仅归还所有权。

(3)    std::auto_ptr 最好不要当成参数传递(读者可以自行写代码确定为什么不能)。

(4)    由于 std::auto_ptr 的“operator=”问题,有其管理的对象不能放入 std::vector 等容器中。

(5)    ……

使用一个 std::auto_ptr 的限制还真多,还不能用来管理堆内存数组,这应该是你目前在想的事情吧,我也觉得限制挺多的,哪天一个不小心,就导致问题了。

由于 std::auto_ptr 引发了诸多问题,一些设计并不是非常符合 C++ 编程思想,所以引发了下面 boost 的智能指针,boost 智能指针可以解决如上问题。

让我们继续向下看。

 

3、boost::scoped_ptr

boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。boost::scoped_ptr 跟 std::auto_ptr 一样,可以方便的管理单个堆内存对象,特别的是,boost::scoped_ptr 独享所有权,避免了 std::auto_ptr 恼人的几个问题。

我们还是从代码开始分析:

void TestScopedPtr() {

boost::scoped_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();

my_memory.release();           // 编译 error: scoped_ptr 没有 release 函数

std::auto_ptr<Simple> my_memory2;

my_memory2 = my_memory;        // 编译 error: scoped_ptr 没有重载 operator=,不会导致所有权转移

}

}

首先,我们可以看到,boost::scoped_ptr 也可以像 auto_ptr 一样正常使用。但其没有 release() 函数,不会导致先前的内存泄露问题。其次,由于 boost::scoped_ptr 是独享所有权的,所以明确拒绝用户写“my_memory2 = my_memory”之类的语句,可以缓解 std::auto_ptr 几个恼人的问题。

由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不了了,如此我们再引入一个智能指针,专门用于处理复制,参数传递的情况,这便是如下的 boost::shared_ptr。

 

4、boost::shared_ptr

boost::shared_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。在上面我们看到 boost::scoped_ptr 独享所有权,不允许赋值、拷贝,boost::shared_ptr 是专门用于共享所有权的,由于要共享所有权,其在内部使用了引用计数。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

boost::shared_ptr 也可以很方便的使用。并且没有 release() 函数。关键的一点,boost::shared_ptr 内部维护了一个引用计数,由此可以支持复制、参数传递等。boost::shared_ptr 提供了一个函数 use_count() ,此函数返回 boost::shared_ptr 内部的引用计数。查看执行结果,我们可以看到在 TestSharedPtr2 函数中,引用计数为 1,传递参数后(此处进行了一次复制),在函数TestSharedPtr 内部,引用计数为2,在 TestSharedPtr 返回后,引用计数又降低为 1。当我们需要使用一个共享对象的时候,boost::shared_ptr 是再好不过的了。

在此,我们已经看完单个对象的智能指针管理,关于智能指针管理数组,我们接下来讲到。

 

5、boost::scoped_array

boost::scoped_array 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。

boost::scoped_array 便是用于管理动态数组的。跟 boost::scoped_ptr 一样,也是独享所有权的。

我们还是从代码开始分析:

void TestScopedArray() {

boost::scoped_array<Simple> my_memory(new Simple[2]); // 使用内存数组来初始化

if (my_memory.get()) {

my_memory[0].PrintSomething();

my_memory.get()[0].info_extend = "Addition";

my_memory[0].PrintSomething();

(*my_memory)[0].info_extend += " other";            // 编译 error,scoped_ptr 没有重载 operator*

my_memory[0].release();                             // 同上,没有 release 函数

boost::scoped_array<Simple> my_memory2;

my_memory2 = my_memory;                             // 编译 error,同上,没有重载 operator=

}

}

boost::scoped_array 的使用跟 boost::scoped_ptr 差不多,不支持复制,并且初始化的时候需要使用动态数组。另外,boost::scoped_array 没有重载“operator*”,其实这并无大碍,一般情况下,我们使用 get() 函数更明确些。

下面肯定应该讲 boost::shared_array 了,一个用引用计数解决复制、参数传递的智能指针类。

 

6、boost::shared_array

boost::shared_array 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。

由于 boost::scoped_array 独享所有权,显然在很多情况下(参数传递、对象赋值等)不满足需求,由此我们引入 boost::shared_array。跟 boost::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 一样,使用了引用计数,可以复制,通过参数来传递。

 

至此,我们讲过的智能指针有 std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array。这几个智能指针已经基本够我们使用了,90% 的使用过标准智能指针的代码就这 5 种。可如下还有两种智能指针,它们肯定有用,但有什么用处呢,一起看看吧。

 

7、boost::weak_ptr

boost::weak_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。

在讲 boost::weak_ptr 之前,让我们先回顾一下前面讲解的内容。似乎 boost::scoped_ptr、boost::shared_ptr 这两个智能指针就可以解决所有单个对象内存的管理了,这儿还多出一个 boost::weak_ptr,是否还有某些情况我们没纳入考虑呢?

回答:有。首先 boost::weak_ptr 是专门为 boost::shared_ptr 而准备的。有时候,我们只关心能否使用对象,并不关心内部的引用计数。boost::weak_ptr 是 boost::shared_ptr 的观察者(Observer)对象,观察者意味着 boost::weak_ptr 只对 boost::shared_ptr 进行引用,而不改变其引用计数,当被观察的 boost::shared_ptr 失效后,相应的 boost::weak_ptr 也相应失效。

我们还是从代码开始分析:

void TestWeakPtr() {

boost::weak_ptr<Simple> my_memory_weak;

boost::shared_ptr<Simple> my_memory(new Simple(1));

 

std::cout << "TestWeakPtr boost::shared_ptr UseCount: " << my_memory.use_count() << std::endl;

my_memory_weak = my_memory;

std::cout << "TestWeakPtr boost::shared_ptr UseCount: " << my_memory.use_count() << std::endl;

}

执行结果为:

Simple: 1

TestWeakPtr boost::shared_ptr UseCount: 1

TestWeakPtr boost::shared_ptr UseCount: 1

~Simple: 1

我们看到,尽管被赋值了,内部的引用计数并没有什么变化,当然,读者也可以试试传递参数等其他情况。

现在要说的问题是,boost::weak_ptr 到底有什么作用呢?从上面那个例子看来,似乎没有任何作用,其实 boost::weak_ptr 主要用在软件架构设计中,可以在基类(此处的基类并非抽象基类,而是指继承于抽象基类的虚基类)中定义一个 boost::weak_ptr,用于指向子类的 boost::shared_ptr,这样基类仅仅观察自己的 boost::weak_ptr 是否为空就知道子类有没对自己赋值了,而不用影响子类 boost::shared_ptr 的引用计数,用以降低复杂度,更好的管理对象。

 

8、boost::intrusive_ptr

boost::intrusive_ptr属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。

讲完如上 6 种智能指针后,对于一般程序来说 C++ 堆内存管理就够用了,现在有多了一种 boost::intrusive_ptr,这是一种插入式的智能指针,内部不含有引用计数,需要程序员自己加入引用计数,不然编译不过(⊙﹏⊙b汗)。个人感觉这个智能指针没太大用处,至少我没用过。有兴趣的朋友自己研究一下源代码哦J。

 

 

三、总结

如上讲了这么多智能指针,有必要对这些智能指针做个总结:

1、在可以使用 boost 库的场合下,拒绝使用 std::auto_ptr,因为其不仅不符合 C++ 编程思想,而且极容易出错[2]。

2、在确定对象无需共享的情况下,使用 boost::scoped_ptr(当然动态数组使用 boost::scoped_array)。

3、在对象需要共享的情况下,使用 boost::shared_ptr(当然动态数组使用 boost::shared_array)。

4、在需要访问 boost::shared_ptr 对象,而又不想改变其引用计数的情况下,使用 boost::weak_ptr,一般常用于软件框架设计中。

5、最后一点,也是要求最苛刻一点:在你的代码中,不要出现 delete 关键字(或 C 语言的 free 函数),因为可以用智能指针去管理。


转自:http://dbchu.com/2013/05/11/966

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值