简单实现C++智能指针

RAII 的基本用法

工厂方法的简单示例

enum class shape_type {
	circle,
	triangle,
	rectangle,
};

class shape{};

class circle :public shape {};
class triangle :public shape {};
class rectangle :public shape {};

create_shape 方法会返回一个 shape 对象,对象的实际类型是某个 shape 的子类。函数的返回值只能是指针或其变体形式。

shape* create_shape(shape_type type) {
	switch (type)
	{
	case shape_type::circle:
			return new circle();
	case shape_type::triangle:
		return new triangle();
	case shape_type::rectangle:
		return  new rectangle();
	default:
		break;
	}
}

智能指针的简单实现

class shape_wrapper{
public:
	explicit shape_wrapper(shape* ptr = nullptr): ptr_(ptr) {
	}
	~shape_wrapper() {
		//delete 时则判断指针是否为空,在指针不为空时调用析构函数并释放之前分配的内存。
		delete ptr_;
	};
	shape* get() const {
		return ptr_; 
	}
private:
	shape* ptr_;
};

new 的时候先分配内存(失败时整个操作失败并向外抛出异常,通常是 bad_alloc),然后在这个结果指针上构造对象(注意上面示意中的调用构造函数并不是合法的 C++ 代码);构造成功则 new 操作整体完成,否则释放刚分配的内存并继续向外抛构造函数产生的异常。delete 时则判断指针是否为空,在指针不为空时调用析构函数并释放之前分配的内存。

这样一个最简单的智能指针就实现了,它有以下问题:

这个类只适用于 shape、类该类对象的行为不够像指针、拷贝该类对象会引发程序行为异常。

那么增加模板声明 template 

template <typename T>
class smart_ptr
{
public:
	explicit smart_ptr(T* ptr = nullptr):ptr_(ptr) {
	}
	~smart_ptr() {
		delete ptr_
	}
	T* get() const { return ptr_; }
private:
	T* ptr_;
};

顺便增加几个成员函数

template <typename T>
class smart_ptr
{
public:
	explicit smart_ptr(T* ptr = nullptr):ptr_(ptr) {
	}
	~smart_ptr() {
		delete ptr_;
	}
	T* get() const { return ptr_; }
	T& operator*() const { return *ptr_; }
	T* operator->() const { return ptr_; }
	operator bool() const { return ptr_; }
private:
	T* ptr_;
};

此时可以调用,但不能拷贝,类似以下这样调用会出错

	smart_ptr<shape> ptr1{
		create_shape(shape_type::circle)
	};
	smart_ptr<shape> ptr2{
		ptr1
	};

改造后,赋值分为拷贝构造和交换两步

template <typename T>
class smart_ptr{
public:
	explicit smart_ptr(T* ptr = nullptr) :ptr_(ptr) {
	}
	~smart_ptr() {
		delete ptr_;
	}

	smart_ptr(smart_ptr& other) {
		//拷贝构造函数中,通过调用 other 的 release 方法来释放它对指针的所有权
		ptr_ = other.release();
	}

	smart_ptr& operator=(smart_ptr& rhs) {
		//赋值函数中,则通过拷贝构造产生一个临时对象并调用 swap 来交换对指针的所有权
		smart_ptr(rhs).swap(*this);
		return *this;
	}

	T* get() const { return ptr_; }
	T& operator*() const { return *ptr_; }
	T* operator->() const { return ptr_; }
	operator bool() const { return ptr_; }

	T* release() {
		T* ptr = ptr_;
		ptr_ = nullptr;
		return ptr;
	}

	void swap(smart_ptr& rhs) {
		using std::swap;
		swap(ptr_, rhs.ptr_);
	}

private:
	T* ptr_;
};

移动语义进一步改造

把拷贝构造函数中的参数类型 smart_ptr& 改成了 smart_ptr&&;现在它成了移动构造函数。

把赋值函数中的参数类型 smart_ptr& 改成了 smart_ptr,在构造参数时直接生成新的智能指针,从而不再需要在函数体中构造临时对象。现在赋值函数的行为是移动还是拷贝,完全依赖于构造参数时走的是移动构造还是拷贝构造。

根据 C++ 的规则,如果我提供了移动构造函数而没有手动提供拷贝构造函数,那后者自动被禁用。

那么,一个 circle* 是可以隐式转换成 shape* 的,但上面的 smart_ptr 却无法自动转换成 smart_ptr。只需更改一处

	template <typename U>
	smart_ptr(smart_ptr<U>&& other) {
		//拷贝构造函数中,通过调用 other 的 release 方法来释放它对指针的所有权
		ptr_ = other.release();
	}

这样,我们自然而然利用了指针的转换特性:现在 smart_ptr<circle> 可以移动给 smart_ptr<shape>,但不能移动给 smart_ptr<triangle>。不正确的转换会在代码编译时直接报错。

下篇介绍同时定义标准的拷贝 / 移动构造函数和所需要的模板函数,并添加引用计数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值