码迷,mamicode.com
首页 > 其他好文 > 详细

技术分享会之——智能指针

时间:2015-07-22 18:46:01      阅读:100      评论:0      收藏:0      [点我收藏+]

标签:c++   智能指针   内存泄露   悬垂指针   

由于之前也只是了解智能指针,要我说估计只能说个它是干什么的,用不了几分钟。

昨天花了一天时间各种百度,算是对智能指针有了一点了解,这篇文章基本就是这次分享会的PPT的copy,没有底层的东西,多是概念。

我觉得理解智能指针需要了解它发展的三个过程:起因,经过,结果。这篇文章主要讲述的是起因,经过和结果等以后工作了,实际接触了再说吧。


起因:

1.为什么需要智能指针

我们先看两个例子

一:内存泄露

<pre name="code" class="cpp">//内存泄露
#include <stdio.h>
#include <iostream>
using namespace std;
class A
{
public:
    A(char *cstr);
    A(const A &ca);
    A();
    ~A();
    A operator=(const A &ca);
private:
    char *str;
};                        
A::A(char *cstr)            
{
    str = new char[20];
    strcpy(str, cstr);
}
A::A(const A &ca)           
{
    str = new char[20];
    strcpy(str, ca.str);
}
A A::operator=(const A &ca)   
{
    str = new char[20];      
    strcpy(str, ca.str);
    return str;
}
A::A() : str(new char[20])
{
    strcpy(str, "Welcome!");
}
A::~A()
{
    delete[] str;
    str = NULL;
}
int main(void)
{
    A a("Hello world!");
    A b(a);
    A c;
    c = b;
	
    return 0;
}

A A::operator=(constA &ca)   
{
    str =new char[20];      
    strcpy(str, ca.str);
    return str;
}
?罪魁祸首就是这段函数,当c=b的时候,就已经发生了内存泄露的隐患。
?原因:
–Ac 这句话调用了构造函数,已经在内存空间new了一块空间。
–c=b 这句调用了操作符重载函数,同样new了一块空间,这样上一次new的空间我们就找不到了。
–即使在析构的时候delet了第二次申请的空间,但是依然无法避免内存泄露的问题。

解决办法:

一:
A::operator=(constA &ca)
{
strcpy(str, ca.str);
}

但是前提是我们已经申请了空间。
二:
A::operator=(const A &ca)   
{
delete[] str;
str = new char[20];
strcpy(str, ca.str);
}
这是个比较危险的方法,如果str没有被初始化或者已经被delet过了,程序就会发生致命错误!
三:
A::operator=(const A &ca) 
{
if(str == NULL)
{          
str = new char[20];
}     
strcpy(str, ca.str);
}
这是最好的方法,可以在很多源码中发现都是用了这种方法。但是需要注意的是str在没有被分配前必须初始化为NULL,因为在未分配和初始化NULL的时候,str不一定是个空指针,还可能是一个野指针。

二:悬垂指针

#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;



int *p=NULL;
void fun()
{
	int i=10;
	p=&i;
}

int main()
{
	fun();
	cout<<"*p= "<<*p<<endl;//输出p=10
	Sleep(1000);
	cout<<"*p= "<<*p<<endl;//输出p=0
	return 0;
}
两次输出的值不一样,因为程序发生了悬垂指针的错误。具体和操作系统的堆栈管理方式有关,感兴趣可以去百度~
在调用fun()函数的时候,指针P指向了这个模块内的一个整型,当这个模块刚刚返回,这个整型还是存在的,但是过了一会(具体多久还要看具体情况),它被系统释放掉了,那么P所指向的内容就不是我们能控制的了,这就是悬垂指针。
悬垂指针的定义:指向曾经存在的对象,但该对象已经不再存在了,此类指针称为悬垂指针。结果未定义,往往导致程序错误,而且难以检测。

以上可以说是智能指针的起因了吧,我做过一些小项目,所以能深深体会到智能指针的必要性。有时候编写程序的时候总会new一些东西,要么忘了销毁,要么搞不清楚在哪里销毁,这也许和我经验不足有关,但是难保一些大神们不会犯这种错。

2.为什么不自建“智能”指针

这里的为什么的主语-“我”特指像我这样的新手。既然new和delete要成对的出现,那我们为什么不自己封装一个类,创建的时候new,析构的时候delete呢?之所以特指,是因为新手考虑问题都比较片面,这样除了同样会造成上面的内存泄露的问题,还会造成其他错误。
class intptr 
{ 
private: 
 int* m_p; 
public: 
 intptr(int* p){ m_p = p; } 
 ~intptr(){ delete m_p; } 
 int& operator*(){ return *m_p; } 
};
这就是上面想法的一个例子,当我们这样调用的时候不会出现问题:
somefunction()
{
 intptr pi(new int); 
 *pi = 10; 
 int a = *pi; 
}
但是如果换种方式:
void somefunction()
{
 intptr pt1(new int);
 intptr pt2(new int);
 *pt1 = 10;
 pt2 = pt1;
}
问题就出现了,pt2指向了pt1所指向的内容,那么pt2原来所指向的内容我们就无法获取和销毁了,这也是一种内存泄露。另外在程序块结束后,这块内存将要被销毁两次,这明显是不符合逻辑的。


经过:

1.引用计数

由于以上问题,我们又引入了引用计数这个概念。
在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。

2.几种智能指针的概述

这里只是概述,不说用法和分析源码
智能指针有很多:
std::auto_ptr、
boost::scoped_ptr、
boost::shared_ptr、
boost::scoped_array、
boost::shared_array、
boost::weak_ptr、
boost::intrusive_ptr

auto_ptr

缺点OR特性:
不能作为STL的成员(C++标准明确禁止这样做,否则可能会碰到不可预见的结果)
不能共享所有权(不一定是缺点,有它一定的应用)
不能指向数组
不能通过赋值操作来初始化
std::auto_ptr<int> p(new int(42)); //OK
std::auto_ptr<int> p = new int(42); //ERROR

scoped_ptr

?boost::scoped_ptr的实现和std::auto_ptr非常类似,都是利用了一个栈上的对象去管理一个堆上的对象,从而使得堆上的对象随着栈上的对象销毁时自动删除。不同的是,boost::scoped_ptr有着更严格的使用限制——不能拷贝。这就意味着:boost::scoped_ptr指针是不能转换其所有权的。

?缺点OR特性:
–不能转换所有权
boost::scoped_ptr所管理的对象生命周期仅仅局限于一个区间(该指针所在的"{}"之间),无法传到区间之外,这就意味着boost::scoped_ptr对象是不能作为函数的返回值的(std::auto_ptr可以)。
–不能共享所有权
这点和std::auto_ptr类似。这个特点一方面使得该指针简单易用。另一方面也造成了功能的薄弱——不能用于stl的容器中。
–不能用于管理数组对象
由于boost::scoped_ptr是通过delete来删除所管理对象的,而数组对象必须通过deletep[]来删除,因此boost::scoped_ptr是不能管理数组对象的,如果要管理数组对象需要使用boost::scoped_array类

shared_ptr

?缺点OR特性:
–1. shared_ptr是Boost库所提供的一个智能指针的实现,shared_ptr就是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针.
–2. shared_ptr比auto_ptr更安全
–3. shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。

weak_ptr

一种弱引用

3.强引用与弱引用

一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。

相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存

当时举的鸭子和鸡的例子这里就不说了


结果:

现在的结果就是我们上面看到那些已经封装好的智能指针,至于为什么没有放到结果这里,是因为我想它还会继续发展,会越来越简单方便。




版权声明:本文为博主原创文章,未经博主允许不得转载。

技术分享会之——智能指针

标签:c++   智能指针   内存泄露   悬垂指针   

原文地址:http://blog.csdn.net/qq369846642/article/details/47005511

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!