C++智能指针的问题

来源:互联网 发布:网络电视机 编辑:程序博客网 时间:2024/06/16 22:48

如何回答C++面试中关于智能指针的问题?

1、  什么是智能指针?

2、  分析下常见的智能指针有哪些?

3、实现一个智能指针呗?(没具体说写哪个,建议默认写:unique_ptr

1、答:智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露(利用自动调用类的析构函数来释放内存)。它的一种通用实现技术是使用引用计数(除此之外还有资源独占,如(auto_ptr),只引用,不计数(weak_ptr))。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。 

2、常见的智能指针以及解析: 

auto_ptr

(1)它是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者(资源独占)。当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。

(2)auto_ptr不能指向数组,因为auto_ptr在析构的时候只是调用delete,而数组应该要调用delete[]。(但uniquearray管理的是一段连续的空间,它也是防拷贝的,功能类似于vector。)

(3)auto_ptr不能作为容器对象,因为它不支持拷贝构造与赋值(出错了也不容易发现),STL容器中的元素经常要支持拷贝,赋值等操作,在这过程中auto_ptr会传递所有权,那么就会出错。

unique_ptr

它是( C++11引入的,前身是scoped_ptr,scoped_ptr是boost库里的),也不支持拷贝构造和赋值,但比auto_ptr好,直接赋值会编译出错(与auto_ptr最大的不同就是类内私有的声明了拷贝构造函数和赋值运算符重载,是针对auto_ptr的缺点而出现的)。

shared_ptr

C++11或boost的shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。环状的链式结构可能会形成内存泄露(循环引用)

循环引用问题(常问到哦)


为了解决类似这样的问题,c++11引入了weak_ptr,来打破这种循环引用。

weak_ptr

C++11或boost的weak_ptr,弱引用。 引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要手动打破循环引用或使用weak_ptr。顾名思义,weak_ptr是一个弱引用,它是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是说,它只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。

weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

auto_ptr的实现

 

template<class T>

class Autoptr //采用资源的转移方法管理内存,在它的拷贝构造和赋值运算符引起会出现问题

{

public:

    Autoptr(T *p=NULL )

        :ptr(p)

    {}

    Autoptr(Autoptr<T>&ap)

        :ptr(ap.ptr)

    {

        ap.ptr= NULL;//新对象构造成功之后,原来对象指针为NULL;保证free的时候释放1

                

    }

 

    Autoptr<T>&operator=(Autoptr<T>&ap)

    {

        if(this!= &ap)

        {

            if(ap.ptr)

            {

                deleteptr;

            }

            ptr= ap.ptr;

            ap.ptr= NULL;

        }

        return*this;

    }

 

    T* operator*()

    {

        return*ptr;

    }

    T* operator->()

    {

        returnptr;

    }

    voidget_ptr()

    {

        returnptr;

    }

    ~Autoptr()

    {

        if(ptr)

        {

            deleteptr;

            ptr= NULL;

        }

    }

private:

    T*ptr;

};

voidfuntest()

{

    int*p = new int(1);

    Autoptr<int>ap1(p);   //只是调了构造函数,没有调拷贝构造函数

    Autoptr<int>ap2(ap1);//调了拷贝构造函数,ap1=NULL

    Autoptr<int>ap3;

   ap3= ap2;//赋值不过去,因为两个对象地址不同,ap2.ptr也不为空,所以调用operator=函数,但是此刻已经释放了

}            //ap3ptr指向的空间,所以没法向ap3赋值

int main()

{

    funtest();

    system("pause");

    return0;

}

unique_ptr的实现

template<class T>

class unique_ptr //前身是scoped_pt,实现粗暴—>禁止转移-->独占资源(只允许一个指针管理资源)

                //(禁止调拷贝构造和赋值运算符),它只能管理单个对象

{

public:

    unique_ptr (T*p = NULL)

        :ptr(p)

    {}

 

   

    T* operator*()

    {

        return*ptr;

    }

    T* operator->()

    {

        returnptr;

    }

    voidget_ptr()

    {

        returnptr;

    }

    ~ unique_ptr ()

    {

        if(ptr)

        {

            deleteptr;

            ptr= NULL;

        }

    }

private:

    unique_ptr (unique_ptr <T>&ap);//禁止拷贝构造

    unique_ptr <T>&operator=( unique_ptr <T>&ap1);//禁止赋值

    T*ptr;

};

//为什么unique_ptr防拷贝的实现必须是私有的声明?

1、只给共有的声明,不给定义。(用户可以在类外重新给出定义)

2、只给私有的定义(但是类内可以调拷贝构造和赋值运算符)

所以采用私有的声明拷贝构造函数和赋值运算符重载函数实现unique_ptr的防拷贝

shared_ptr的实现

template<class T>

class Share_Ptr

{

public:

    Share_Ptr(T*p = NULL)

        :_p(p)

        ,_pcount(NULL)

    {

        if(NULL != _p)

        {

            _pcount= new int(1);//构造成功时有一个对象用,初始化为1

        }

    }

 

    Share_Ptr(const Share_Ptr<T>&ps)

        :_p(ps._p)

        ,_pcount(ps._pcount)

    {

        if(NULL != _pcount)

        {

            ++*_pcount;

        }

    }

 

    T* operator*()

    {

        return*_p;

    }

    T* operator->()

    {

        return_p;

    }

    voidget_ptr()

    {

        return_p;

    }

 

    ~Share_Ptr()

    {

        if(NULL!=_pcount&&0 ==*(--_pcount))

        {

            delete_pcount;

            _pcount= NULL;

            delete_p;

            _p= NULL;

        }

    }

private:

    T* _p;

    int*_pcount;

};

 

voidFunTest()

{

    Share_Ptr<int>ps1(new int);//ps1->_pcount=1;

    Share_Ptr<int>ps2(ps1);//ps2->_pcount=2;

    Share_Ptr<int>ps3;

    ps3= ps2;//ps3->_pcount=2;

}