C++智能指针,指针容器原理及简单实现(auto_ptr,scoped_ptr,ptr_vector).

C++智能指针,指针容器原理及简单实现(auto_ptr,scoped_ptr,ptr_vector).


前言

最近再写一个muduo的异步日志接触了很多智能指针,但是又不打算用boost库,只好模一个来用用了.

智能指针的本质即用栈上对象来管理堆上数据的生命周期.

智能指针本身是一个对象,它在栈上创建,构造的时候分配堆上资源,析构的时候释放资源,这样就避免了堆上数据资源泄露的情况.
同时重载它的-> 和 * 运算符实现如同裸指针一样的操作.

下面看看几个局部智能指针对象的实现代码。

auto_ptr

auto_ptr特点: 实现拷贝构造函数, 重载 = 运算符, 实现->、* 运算符, 使它能够像普通指针一样 使用,
同时通过release() 和 reset() 方法实现安全的转移使用权 .

#ifndef _AUTO_PTR_HH
#define _AUTO_PTR_HH

template<typename T>
class auto_ptr{
public:
	explicit auto_ptr(T* p = 0):m_ptr(p){printf("1\n");
	}
	
	auto_ptr(auto_ptr& obj):m_ptr(obj.release()){printf("2\n");
	}
	
	auto_ptr& operator=(auto_ptr& obj){printf("3\n");
		reset(obj.release());
		return *this;
	}
	
	~auto_ptr(){printf("4\n");
		delete m_ptr;
	}

	T* release(){
		T* tmp = m_ptr;
		m_ptr = 0;
		return tmp;
	}
	
	void reset(T* p){
		if(m_ptr != p)
			delete m_ptr;
		m_ptr = p;
	}
	
	T* get() const {
		return m_ptr;
	}
	
	T* operator->(){
		return get();
	}
	
	T& operator*(){
		return *get();
	}
	
private:
	T* m_ptr;
};

#endif

测试代码:

#include "ScopePtr.hh"
#include "auto_ptr.hh"
#include <stdio.h>

class NonCopyable
{
protected: //构造函数可以被派生类调用,但不能直接构造对象
    NonCopyable() {printf("Nocopy Constroctr\n");}
    ~NonCopyable() {printf("~Nocopy DeConstroctr\n");}
private:
    NonCopyable(const NonCopyable &);
    const NonCopyable &operator=(const NonCopyable &);
};


class Test// : private NonCopyable{
{public:
	Test(){printf("Constroctr\n");}
	~Test(){printf("~DeConstroctr\n");}
};

int main(){
	
	//scoped_ptr<Test> st(new Test);
	
	auto_ptr<Test> ap1(new Test);
	auto_ptr<Test> ap2(new Test);

	auto_ptr<Test> ap3(ap2);
	
	ap2 = ap3;
	
	getchar();
	return 0;
}


Constroctr
1
Constroctr
1
2
3

4
4
~DeConstroctr
4
~DeConstroctr

scoped_ptr

这个是boost库里面的东西,它和auto_ptr正相反: 将拷贝构造和=重载 都配置为私有,已达到不允许转移拥有权的目的.

#ifndef _SCOPE_PTR_HH
#define _SCOPE_PTR_HH
//  scoped_ptr mimics a built-in pointer except that it guarantees deletion
//  of the object pointed to, either on destruction of the scoped_ptr or via
//  an explicit reset(). scoped_ptr is a simple solution for simple needs;
//  use shared_ptr or std::auto_ptr if your needs are more complex.

/*
scoped_ptr 是局部智能指针 不允许转让所有权。
*/
template <class T>
class scoped_ptr
{
public:
	scoped_ptr(T *p = 0) :m_ptr(p) {
	}
	
	~scoped_ptr(){
		delete m_ptr;
	}
	
	T&operator*() const {
		return *m_ptr;
	}
	
	T*operator->() const {
		return m_ptr;
	}
	
	void reset(T *p)//拥有权不允许转让  但是可以让智能指针指向另一个空间  
	{
		if (p != m_ptr && m_ptr != 0)
			delete m_ptr;
		m_ptr = p;
	}

	T* get(){
		return m_ptr;
	}

private://将拷贝构造和赋值  以及判等判不等  都设置为私有方法
	//对象不再能调用,即不能拷贝构造和赋值  也就达到了不让转移拥有权的目的
	scoped_ptr(const scoped_ptr<T> &y);
	scoped_ptr<T> operator=(const scoped_ptr<T> &);
	void operator==(scoped_ptr<T> const &) const;
	void operator!=(scoped_ptr<T> const &) const;

	T* m_ptr;
};

#endif

ptr_vector

这个也是boost里面的东西,如果我们光放对象指针到vector里面,容器析构的时候虽然会析构自己开辟出来的存放指针的空间,但不会析构指针本身指向的空间,于是有了这个容器.

#ifndef _PTR_VECTOR_HH
#define _PTR_VECTOR_HH

#include "auto_ptr.hh"
#include <vector>

template<typename T>
class ptr_vector : public std::vector<T*>{
public:
	~ptr_vector(){
		clear();
	}

	void clear(){
		typename std::vector<T*>::iterator it;
		for(it = std::vector<T*>::begin(); it != std::vector<T*>::end(); ++it){
			delete *it;//释放指针指向的内存.
		}
		
		/*
		for(size_t i = 0; i < std::vector<T*>::size(); ++i){
			delete std::vector<T*>::back();
		}*/
		
		std::vector<T*>::clear(); //释放指针本身.
	}

	typename std::vector<T*>::iterator erase(typename std::vector<T*>::iterator it){
		if(it >= std::vector<T*>::begin() && it < std::vector<T*>::end()){
			delete *it;
			std::vector<T*>::erase(it);
		}
	}

	void pop_back(){
		if(std::vector<T*>::size() > 0){
			delete std::vector<T*>::back();
			std::vector<T*>::pop_back();
		}
	}
	
	void push_back(T* const &v){
		auto_ptr<T> ap(v);
		std::vector<T*>::push_back(v);
		ap.release();
	}

	void push_back(auto_ptr<T> &v){
		std::vector<T*>::push_back(v.get());
		v.release();
	}
	
};

#endif

测试代码:


class Test// : private NonCopyable{
{public:
	Test(int a = 99):a(a){printf("Constroctr\n");}
	~Test(){printf("~DeConstroctr\n");}
	int get(){return a;}
private:
	int a;
};

int main(){
	auto_ptr<Test> ap1(new Test(0));
	auto_ptr<Test> ap2(new Test(1));
	auto_ptr<Test> ap3(new Test(2));

	printf("%d\n", ap1->get());
	
	ptr_vector<Test> apv;
	apv.push_back(ap1);
	apv.push_back(ap2);
	apv.push_back(ap3);
	printf("%d %lu \n", apv.front()->get(),apv.size());
/*
	apv.pop_back();
	printf("%lu\n", apv.size());

	apv.pop_back();
	printf("%lu\n", apv.size());
	
	apv.pop_back();
	printf("%lu\n", apv.size());
*/
	apv.pop_back();
	printf("%lu\n", apv.size());
	
	
	ptr_vector<Test>::iterator it = apv.begin();
	apv.erase(it);
	printf("%lu\n", apv.size());

	
	getchar();
	
	
	
	return 0;
}


Constroctr
Constroctr
Constroctr
0
0 3 
~DeConstroctr
2
~DeConstroctr
1

~DeConstroctr

本文主介绍了智能指针的本质,及两种简单的智能指针实现与一个指针容器的实现.

事实上现在auto_ptr用的不多,如果没对原来传进来的指针进行处理,转移后,原来的指针为空了,如果有人去使用既会造成问题。
vector也存在很多问题,pop_back()一个空的容器,vector里面照样会做--size,这时候容器大小从0就变成了无限大,后果无法预料,.本例中对这种情况进行了处理. pop_back()一个空的vector将什么都不做. 但是vector用法还是有讲究的,不然容易造成问题.

posted @ 2018-08-09 21:08  靑い空゛  阅读(1985)  评论(0编辑  收藏  举报