stl源码剖析(二)迭代器之智能指针

前言:智能指针auto_ptr(只能指向动态内存)(也是unique_ptr的一种)

auto_ptr是c++标准库的类模板,auto_ptr对象通过初始化指向new创建的动态内存,auto_ptr对象即为这块内存的拥有者,一块内存不能有两个拥有者。

当auto_ptr对象的生命周期结束时,析构函数会将auto_ptr对象拥有的动态内存自动释放,防止内存泄漏。


内存泄漏

  1. // 示例1(a):原始代码    
  2. void f()  
  3. {  
  4. T* pt( new T );  
  5.       /*...更多的代码...*/  
  6.       delete pt;  
  7. }  
若在delete之前就返回,则分配的对象没有被删除,就是一个经典的内存泄漏。

解决:

  1. // 示例1(b):安全代码,使用了auto_ptr  
  2. void f()  
  3. {  
  4. auto_ptr pt( new T );  
  5.       /*...更多的代码...*/  
  6. // 酷:当pt出了作用域时析构函数被调用,从而对象被自动删除  


使用方法:

头文件:#include


初始化:

1.构造函数

1>.直接:auto_ptr api(new int(22));

2>.用普通指针构造: int *p=new int(22);    auto_ptr api(p);

2.拷贝构造:用已存在的智能指针构造新的智能指针:

auto_ptr ap_string(new string("keke"));

auto+ptr ap_stringnew(ap_string); //构造或赋值会造成拥有权转移,ap_string失去对keke的所有权,由ap_stringnew获得,

对象销毁时,ap_stringnew负责内存的自动销毁。

3.赋值

auto_ptr p1(new int(1024));

auto_ptr p2(new int(2048));

p1=p2; //赋值前,p1指向对象被删除,赋值后,p2不再指向该对象,p1拥有2048这块内存的控制权


4.智能指针作为参数传递很危险:

void *f(auto_ptr ap)

{

cout<<*ap;

}

auto_ptr ap1(new int(0));

f(ap1);

cout<<*ap1 //error,经过f(ap1),由于是值传递,函数作用域中会产生一个临时对象,接收ap1指向的内存,

而在离开该函数f()时,临时对象调析构函数将该内存释放,故ap1不再指向有效内存,访问失败

解决办法:引用或指针,为了防止在函数中对其操作,用const reference作为参数传递:void *f(const auto_ptr &ap)

auto_ptr不能被用作容器中:vector>  m_example;

因为进行值传递时,当从函数返回时,容器中的元素会被置位为NULL



5.auto_ptr中常用成员函数

1>get() :返回auto_ptr指向的对象的内存地址

int *p=new int(22);

auto_ptr ap1(p);

cout< (int所在的那块内存地址)

cout<<&ap1 是ap1对象本身所在的内存地址


2>reset() :重新设置auto_ptr指向的对象

auto_ptr pstr_auto(new string("keke"));

pstr_auto.reset(new string("love")); //先释放keke的内存空间,再指向love内存空间


3>release() 手动撤销资源:返回auto_ptr指向的对象的内存地址,并释放对这个对象所有权(防止两个对象指向同一地址)

auto_ptr pstr_auto(new string("keke"));

auto_ptr pstr_auto2(pstr_auto.get()); //两个auto_ptr对象指向同一地址,对象释放时出现问题,delete两次

auto_ptr pstr_auto2(pstr_auto.release());//release先释放所有权,再有pstr_auto2掌控内存所有权


auto_ptr实现关键点:

1.利用:栈上对象在离开作用域时会自动析构

2.编译器比人可靠,析构交给编译器

3.auto_ptr在栈上构建一个对象a,a中封装了动态分配内存的指针p,重载了operator*,operator->,

使对对象a操作其实就是对指针p操作,而在a的析构函数中自动释放p的空间,析构函数是编译器自动调用的。


auto_ptr缺点:

1.不适合于数组,不能大规模使用

2.指针托管权转移:auto_ptr apt(new T);

auto_ptr apt1=apt; 此时对apt访问会导致内存错误;(解决:引用计数,boost::shared_ptr)

auto_ptr不能用于vector的原因:(auto_ptr也不能用于STL算法,如sort,find等)

#include
#include
#include
using namespace std;
class D
{
public:
D():d(1){}
~D(){cout<<"D destruction"<int getvalue()
{
return d;
}
private:
int d;
};


void doNothing(auto_ptr d) //每次调用,将实参指向的内存地址指向临时变量d;d在函数结束时析构,释放内存空间
{
cout<<"donothing"<

}
int main()
{
vector >auto_vec;
int i=0;
for(i=0;i<3;i++)
{
auto_vec.push_back(auto_ptr(new D));
}
for(i=0;i<3;i++)//get()函数返回智能指针指向的内存空间地址
cout<
//传值(调用拷贝构造函数)
for(i=0;i<3;i++)
doNothing(auto_vec[i]);

for(i=0;i<3;i++)
cout<
auto_vec.clear();
for(i=0;i<3;i++)
auto_vec.push_back(auto_ptr(new D));

//赋值(调用auto_ptr类的赋值运算符)
for(i=0;i<3;i++)
auto_ptr temp=auto_vec[i];

for(i=0;i<3;i++)
cout< }

输出结果:

00897F18
00897F78
00897F48
donothing
D destruction //传值后,临时对象超出作用域,析构,地址释放
donothing
D destruction
donothing
D destruction
00000000
00000000
00000000
D destruction //赋值操作,临时对象超出作用域,析构
D destruction
D destruction
00000000
00000000
00000000
请按任意键继续. . .

解决办法:使用shared_ptr

输出:

00897F18
00897F78
00897F48
donothing
D destruction
donothing
D destruction
donothing
D destruction
00000000
00000000
00000000
D destruction
D destruction
D destruction
00000000
00000000
00000000
请按任意键继续. . .


shared_ptr和wak_ptr的原理

共享对象的所有权;初始化一个shared_ptr后,可以使其他shared_ptr实例共享这个对象指向的内存块,

当引用计数为0时,删除内存资源和自身

shared_ptr s1(new string);

shared_ptr s2=s1;

weak_ptr w1=s2;


s1,s2,w1都指向同一个ptr_manage的对象。

shared_ref_cnt和weak_ref_cnt分别指向引用它的shared_ptr和weak_ptr的个数

(在复制构造和赋值时对相应的引用值+1,在析构中对相应的引用值-1即可),ptr_manage中的ptr存放真正的对象指针地址,指向object。


1.当shread_ref_cnt=0时,自动释放ptr指针指向的对象object,ptr=0x00000000,但不释放ptr_manage;

2.当shared_ref_cnt和weak_ref_cnt都为0时,才释放ptr_manage对象;


对象函数原型: 
template explicit shared_ptr(T* p); 
在ptr_manage对象中,比较重要的几个成员函数包括: 
shared_ptr(const shared_ptr & r); //构造函数 
void reset();//可以释放该指针,使得对象的引用计数减一; 
T* get();//提供直接访问其原始指针的方法,用于向下兼容。 
T& operator*() const;//提领操作 
T* operator->() const;//指针操作 
bool unique() const;//判断是否为唯一拥有对象 
long use_count() const;//返回引用计数值 
shared_ptr& operator=(const shared_ptr& r );//赋值


weak_ptr:依照上图,weak_ptr与object的生命周期无关,在使用之前很object很可能已经被释放掉,故要先lock。

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,但可以使用lock获得一个可用的shared_ptr对象,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况.






你可能感兴趣的:(STL源码剖析)