Lesson 37 Smart Pointers

Memory leaks (the infamous Bug)
- Dynamic application heap, exhausted not return
-C ++ language, no garbage collection mechanism (java, C # garbage collection mechanism)
- pointer can not be controlled within the meaning of the life cycle of heap space

#include <iostream>
#include <string>

using namespace std;

class Test
{
    int i;
public:
    Test(int i)
    {
        this->i = i;
    }
    int value()
    {
        return i;
    }
    ~Test()
    {
    }
};

int main()
{
    for(int i=0; i<5; i++)
    {
        Test* p = new Test(i);  
        
        cout << p->value() << endl;
    }
    
    return 0;
}

This program will result in the disclosure of memory, are you referring to the application stack space, but there is no release. p is a local variable, after the for loop is gone, but it refers to the heap space does not disappear, you can not use this piece of the heap space.

Thinking depth of
what we need
- the need for a special pointer
- pointer at the end of the life cycle of the initiative to release the heap
- a heap at most only by a pointer identification. (Release times to avoid problems, if two pointers point to a stack space, then it is likely that the space is freed twice)
- to prevent pointer arithmetic and pointer comparison (to avoid dangling pointers and pointer cross-border)

Solution (simulated by an object pointer)
- Overload pointer operator characteristic (-> and *)
- only through overloaded member function of a class
- overloaded function can not use the parameters
- you can only define one overloads

After using the object pointer alternative, the object has a high-end atmosphere on the grade name, smart pointers.

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    int i;

public:
    Test(int i)
    {
        cout << "Test(int i)" << endl;
        this->i = i;
    }
    int value()
    {
        return i;
    }
    ~Test()
    {
        cout << "Test(int i)" << endl;
    }
};

class Pointer
{
private:
    Test* mp;
public:
    Pointer(Test*p = NULL)
    {
        mp = p;
    }
    Test* operator ->()
    {
        return mp;
    }
    Test& operator *()
    {
        return *mp;
    }
};
int main()
{

    for(int i=0; i<5; i++)
    {
        //Test* p = new Test(i);
            Pointer p = new Test(i);
        cout << p->value() << endl;
    }

    return 0;
}

从运行结果看,该智能指针并没有自动调用析构函数来释放堆空间。

 

 在Pointer类中加上析构函数,再次运行:

 

 此时析构函数被调用,解决了内存泄露的问题。

再次看我们的需求,前两条已经被实现:

-需要一个特殊的指针(特殊的指针就是一个对象,通过一个对象来代替指针,通过一个对象来模拟指针的行为)
-指针生命周期结束时主动释放堆空间
-一片堆空间最多只能由一个指针标识。(可以避免多次释放的问题,如果两个指针指向了一片堆空间,那么很可能该空间被释放两次,)
-杜绝指针运算和指针比较(避免指针越界和野指针)

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    int i;

public:
    Test(int i)
    {
        cout << "Test(int i)" << endl;
        this->i = i;
    }
    int value()
    {
        return i;
    }

    ~Test()
    {
        cout << "~Test" << endl;
    }
};

class Pointer
{
private:
    Test* mp;
public:
    Pointer(Test*p = NULL)
    {
        mp = p;
    }
    Pointer(const Pointer& obj)
    {
        //delete mp;这个地方一定不要加上这句,因为这是在拷贝构造函数中,mp还不存在,它是一个野指针。不可能去删除一个野指针。
          mp = obj.mp;  //当前对象的成员指针也指向了初始化对象的成员指针所对应的堆空间,此时意味着两个指针指向了同一片堆空间。
          const_cast<Pointer&>(obj).mp = NULL;//将初始化对象的成员指针所指向的堆空间指为空,意味着初始化对象将它所指向的堆空间完全交给了当前对象,实际上是一个所有权的传递。
      //obj.mp = NULL; 因为obj是const的,不能这样直接赋值。需要利用const_cast将初始化对象的只读属性去掉。
    }
    Pointer& operator = (const Pointer& obj)
    {
        if(this != &obj)
        {
            delete mp; //这个地方为什么可以,因为这是赋值操作符的重载,体现在赋值上,说明当前对象已经存在了,mp可能指向了某个内存空间。
                mp = obj.mp;
            //obj.mp = NULL;
                const_cast<Pointer&>(obj).mp = NULL;
        }

        return *this;
    }
    Test* operator ->()
    {
        return mp;
    }
    Test& operator *()
    {
        return *mp;
    }
    bool isNULL()
    {
        return (mp == NULL);
    }
    ~Pointer()
    {
        delete mp;
    }
};
int main()
{

    Pointer p1 = new Test(0);
    cout << p1->value() << endl;
    Pointer p2 = p1;
    cout << p1.isNULL() <<endl;
    cout << p2->value() << endl;

    return 0;
}

 

 在main函数中,再进行指针比较,看看编译能否通过。例如p2++ ,  if(p1==p2),这样的指针运算是无法编译的。说明了我们已经按照上面的那4条需求实现了一个智能指针。

但是这个智能指针所对应的类仅仅指向Test这个固定的类型,它没有办法指向其他的类型,那能否对其修改,使得Pointer指向各种各样的类型呢?其实是可以的,只不过现在我们还不具备这种技术。这个地方使用模板就可以做到。

智能指针的使用军规:

只能用来指向堆空间中的对象或者变量。

小结:
指针特征操作符(-> 和*)可以被重载
重载指针特征符能够使用对象代替指针
智能指针只能用于指向堆空间中的内存
智能指针的意义在于最大限度的避免内存问题

Guess you like

Origin www.cnblogs.com/-glb/p/11932180.html
Recommended