C++智能指针!

一、什么是智能指针?什么是裸指针?

1、了解智能指针之前,我们先了解什么是裸指针?
凡是带*的都是裸指针,也就是我们C和C++中一直接触的指针。但是指针虽然好用,但是当它在堆上开辟内存之后,如果使用之后处理不当都会引起一系列的问题。比如:

(1)如果得不到及时的手动释放,会造成内存泄漏。

(2)多次释放同一块资源,产生野指针,甚至有可能导致程序崩溃。

(3)写了释放资源的语句,但却从之前return出去了。

但在C++ 11中有了一个新的概念“智能指针”

2、什么是智能指针?
智能指针是存储指向动态分配(堆)对象指针的类,把一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一个对象

而智能指针的智能体现在无论如何,保证资源一定会释放。智能指针并不是一个指针,而是一个模板。由智能指针这个模板实例化出来的类的对象具有和裸指针相似的行为,但是它能够自动的释放所指向的对象,所以我们称之为智能指针。如果我们用普通指针来创建一个指向某个对象的指针,那么我们最后必须要手动释放这块空间,而智能指针它是一个实例化出来的对象,它释放空间是通过自己类内的析构函数完成的。

智能指针原理:利用栈上的对象出作用域自动析构的特点,把资源释放的代码,放在智能指针的析构函数里面。智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放由它管理的堆内存。

二、智能指针实现:

1、我们以前使用裸指针的情况:

int main()
{
    int *p = new int[10];
    /*
    ......
    */
    delete []p;
    /*
    如果你只在堆上申请内存之后,忘记写delete,往往会导致内存泄漏。
    或者别的问题有可能会导致程序崩溃。
    */
    return 0;
}

2、简单智能指针实现:

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T *ptr = nullptr) :mptr(ptr)
    {
        cout << "CSmartPtr()" << endl;
    }
	~CSmartPtr() 
    {   
     cout << "~CSmartPtr()" << endl;
     delete mptr; 
    }
private:
	T *mptr;
};

int main()
{
	CSmartPtr<int> ptr(new int);	
	return 0;
}

可以看到创建的ptr对象先调用了构造函数然后调用了析构函数。因为ptr是栈上的智能指针对象,在构造函数中初始化资源,不管是函数正常执行完,还是运行过程中出现异常,该对象都会自动调用析构函数,在析构函数中进行了delete操作,释放资源。

所以我们可以了解到智能指针对象可以在栈上定义,那么可不可以在堆上定义智能指针呢?

CSmartPtr<int> *ptr2 = new CSmartPtr<int>(new int);

从代码中我们可以看到定义ptr2对象时出现了*,所以看起来它好像是定义了一个智能指针,但实际上还是一个裸指针,需要我们进行手动释放资源。所以说是不可以在堆上定义智能指针的。

3、为智能指针提供重载函数

前面我们已经说过了,智能指针不是指针,而是为了弥补指针有可能带来的不足,对程序造成的困扰而封装的一个类。所以其实它就是在模仿指针的功能,但同时又弥补了不足。但在C++中,我们为了使智能指针用起来与裸指针一样。所以我们必须提供*和->运算符的重载函数来完善其功能。

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T *ptr = nullptr) :mptr(ptr) {}
	~CSmartPtr() { delete mptr; }

	T& operator*() { return *mptr; }
	const T& operator*()const { return *mptr; }

	T* operator->() { return mptr; }
	const T* operator->()const { return mptr; }
private:
	T *mptr;
};
int main()
{
	CSmartPtr<int> ptr(new int);
	*ptr = 10;
	cout << *ptr << endl;
	return 0;
}

提供重载函数之后,我们就可以通过 * 来得到指针所对应的值,这时的使用就和裸指针基本一样了。但是它也有存在的问题。

int main()
{
	CSmartPtr<int> ptr1(new int);	
	CSmartPtr<int> ptr2(ptr1);	
	//*ptr = 10;
	//cout << *ptr << endl;
	return 0;
}

如果我们用已初始化的智能指针ptr1来初始化ptr2,程序运行之后会崩溃掉。为什么呢?因为程序的默认拷贝构造函数做的是浅拷贝。所以用ptr2来初始化ptr1时,两个智能指针是指向同一块内存的。程序结束之后,ptr2先被析构,此时ptr2和ptr1指向的同一块内存已经被析构过一次。但是程序结束的时候ptr1也是要被析构的,所以造成程序崩溃。

针对上面这个问题,要解决智能指针的两大问题:

(1)怎么解决智能指针的浅拷贝问题?

(2)怎么使得多个智能指针指向同一块资源时,而只被释放一次资源?

猜你喜欢

转载自blog.csdn.net/Disremembrance/article/details/89000736