跳至主要內容

不带引用计数的智能指针

张威大约 5 分钟c/c++智能指针

不带引用计数的智能指针

1. 基础知识

裸指针

int main() {
	int* p = new int(10);
	*p = 30;
	delete p;
	
	return 0;
}
  • 如果我们,会造成内存泄漏;
  • 或者由于中间程序的执行顺序和我们预想的不一样,导致,导致内存资源泄漏。

堆内存是我们用户手动开辟,手动释放 用不好裸指针也是造成我们内存资源的一种因素

智能指针

智能指针本身是一个类模板,原理利用栈上的对象出作用域会自动析构这么一个特点,把资源释放的代码全部放在这个析构函数中执行,就达到了所谓的智能指针。

//智能指针就是对普通指针的一个封装
template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T *ptr = nullptr) :mptr(ptr) {}
	~CSmartPtr() { delete mptr; }
private:
	T *mptr;
};

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

1)智能指针体现在把裸指针进行了一次面向对象的封装,在构造函数中初始化资源地址,在析构函数中负责释放资源 2)利用栈上的对象出作用域自动析构这个特点,在智能指针的析构函数中保证释放资源

上面的代码实现的是一个简单的智能指针,能够实现内存的自动释放。CSmartPtr<int> ptr(new int); 这行代码中出现了new,可能会有朋友会误认为智能指针是定义在堆(heap)上的。其实不是,

思考:能不能在堆上定义智能指针? 例如:

CSmartPtr *p = new CSmartPtr(new int)

我们可以看到。智能指针初始化的是一个对象,而不是指针。

  • 使用时,我们不能直接把智能指针定义在堆上,这样就没意义了。
#include <iostream>
using namespace std;

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* p = nullptr) :ptr(p) {}
	~CSmartPtr()
	{
		delete ptr;
	}
	T& operator*()
	{
		return *ptr;
	}
	T* operator->()	//箭头运算符的重载
	{
		return ptr;
	}
private:
	T* ptr;
};

class Test
{
public:
	void show()
	{
		cout << "liufeng" << endl;
	}
};

int main()
{
	CSmartPtr<int>p(new int);
	*p = 20;
	cout << *p << endl;

	CSmartPtr<Test>q(new Test());
	q->show();	
	(q.operator->())->show();

	return 0;
}

2. 不带引用计数的智能指针

智能指针使用时,包含#include <memory>头文件

不带引用计数的智能指针:

  • auto_ptr:C++库里面
  • scoped_ptr 和 unique_ptr : C++11新标准

不带引用计数的智能指针怎么解决浅拷贝问题?

2.1 auto_ptr

int main()
{
	auto_ptr<int> p1(new int);
	auto_ptr<int> p2 = p1;
	*p1 = 10;
	return 0;
}

上面这段代码最终会报错。p1指向了堆上开辟的一块内存,然后用p1拷贝构造p2。我们看了auto_ptr 的源码后得知,auto_ptr 在做拷贝构造函数时,做的是浅拷贝,并且将p2指针指向p1的内存空间,再将

auto_ptr怎么解决浅拷贝问题?

  • 在源码中,不带引用计数,成员变量只有一个裸指针;
  • 怎么管理资源的?永远让最后一个智能指针管理资源,前面的智能指针全部被置为nullptr; 这也就造成上面程序的p1不能使用的情况;

容器中不能使用auto_ptr

int main()
{
	vector<auto_ptr<int>> vec;
	vec.push_back(auto_ptr<int>(new int(10)));
	vec.push_back(auto_ptr<int>(new int(20)));
	vec.push_back(auto_ptr<int>(new int(30)));
	cout << *vec[0] << endl;
	vector<auto_ptr<int>> vec2 = vec;
	cout << *vec[0] << endl;
	return 0;
}

使用容器难免会涉及到容器的拷贝。上面的代码将auto_ptr运用在容器中,第一句cout << *vec[0] << endl;可以正常打印出10,但是第二句cout << *vec[0] << endl;无法正确打印。这是由于vector<auto_ptr<int>> vec2 = vec;

2.2 scoped_ptr

  • 比auto_ptr还暴力,使用的也比较少。
  • 直接 将 了;

2.3 unique_ptr

  • 不带引用技术的智能指针,推荐使用unique_ptr
  • 定义: 只让一个智能指针管理资源,不会让多个智能指针管理资源(编译报错);

也 将 了:

template<typename T>
unique_ptr<T> getSmartPtr()
{
	unique_ptr ptr(new T());
	return ptr;
}

int main()
{

	unique_ptr<int> p1(new int);
    
    //unique_ptr<int> p2(p1); 不能拷贝构造
	unique_ptr<int> p2(std::move(p1)); //转为右值就可以进行拷贝构造了
	
    unique_ptr<int> p3 = getSmartPtr<int>(); // 右边是得到unique_ptr临时对象是右值,故可调用拷贝构造
	return 0;
}