【C++进阶知识】05 - 内存管理、new、智能指针

1. 内存管理

C++的内存管理大致可分为以下几种:
(1)堆的内存管理 – 使用newdelete进行,由内存管理器完成内存分配、内存对齐、内存合并等操作。
(2)栈的内存管理 – 通过栈指针在栈帧上的移动进行分配,栈顶是逐渐靠近低地址位的,不会出现内存碎片,通过栈展开防止异常不能析构的情况。

说到内存管理不能少说的东西:
(1)数组
(2)GC,垃圾回收
(3)各种池技术,如对象池、内存池等
(4)new和delete
(5)智能指针

2. new

2.1 new的本质

当我们new一个对象时,编译器会做如下事情:

/** new T(…) */
{
	// void *operator new(size_t) throw(bad_alloc);
	// 分配对应的sizeof(T)内存,如果失败,则会抛出bad_alloc异常
	void* temp = operator new(sizeof(circle));
	try 
	{
		// 强制转化为T类型
		T* ptr = static_cast<T*>(temp);
		// 调用T类型的构造函数
		ptr->T();
		return ptr;
	}
	catch (...) 
	{
		// 如果有异常,则delete
		operator delete(ptr);
		throw;
	}
}
/* delete T **/
if (ptr != nullptr) 
{
	ptr->~T();
	operator delete(ptr);
}

2.2 应用场景

使用new进行返回值时,需要对其构造和析构函数进行封装

template <typename T>
class T_Wrapper 
{
public:
	/** 封装的接收返回值是T指针的构造函数 */
	explicit T_wrapper(T* ptr = nullptr) : ptr_(ptr) 
	{}
	/** 析构函数 */
	~shape_wrapper()
	{
		delete ptr_;
	}
	
	T* get() const 
	{ 
		return ptr_; 
	}
private:
	T* ptr_;
};

T* Create_T()
{
	return new T;
}

void f()
{
	T_Wrapper ptr_wrapper(Create_T());
}

可以发现此处的T_Wrapper很类似于智能指针,承担了管理任意T对象的指针,防止其内存泄漏的作用。即使用基于栈和析构函数的RAII来管理堆内存。

3. 智能指针的本质

直接手动实现一个基础的智能指针,其本质就能很清晰的呈现在面前,在下述代码中会有适当的注释:

/** 
 * 计数类
 * 用于存储引用计数
 */
class shared_count {
public:
	// 构造时自动赋值为1
	shared_count() noexcept : count_(1) 
	{}
	// 计数增加
	void add_count() noexcept
	{
		++count_;
	}
	// 计数减少,为了和0比较,因此返回
	long reduce_count() noexcept
	{
		return --count_;
	}
	long get_count() const noexcept
	{
		return count_;
	}
private:
	long count_;
};

/** 
 * 实现智能指针,模拟其所有功能
 */
template <typename T>
class smart_ptr 
{
public:
	// 声明友元类,用于访问私有变量
	template <typename U>
	friend class smart_ptr;
	// 
	explicit smart_ptr(T* ptr = nullptr) : ptr_(ptr)
	{
		if (ptr) 
		{
			shared_count_ = new shared_count();
		}
	}
	// 析构函数,如果计数为0并且ptr_存在时,进行析构
	~smart_ptr()
	{
		if (ptr_ && ! shared_count_->reduce_count()) 
		{
			delete ptr_;
			delete shared_count_;
		}
	}
	// 拷贝构造函数,引用计数加1
	smart_ptr(const smart_ptr& other)
	{
		ptr_ = other.ptr_;
		if (ptr_) 
		{
			other.shared_count_->add_count();
			shared_count_ = other.shared_count_;
		}
	}
	// 拷贝构造函数,使用模板,用于父子类的拷贝构造
	template <typename U>
	smart_ptr(const smart_ptr<U>& other) noexcept
	{
		ptr_ = other.ptr_;
		if (ptr_) 
		{
			other.shared_count_->add_count();
			shared_count_ = other.shared_count_;
		}
	}
	// 移动构造函数,将other的指针置空,同时引用计数不变
	template <typename U>
	smart_ptr(smart_ptr<U>&& other) noexcept
	{
		ptr_ = other.ptr_;
		if (ptr_) 
		{
			shared_count_ = other.shared_count_;
			other.ptr_ = nullptr;
		}
	}
	// 用于实现强制类型转换,由U类型转换到T
	template <typename U>
	smart_ptr(const smart_ptr<U>& other, T* ptr) noexcept
	{
		ptr_ = ptr;
		if (ptr_) 
		{
			other.shared_count_->add_count();
			shared_count_ = other.shared_count_;
		}
	}
	// 赋值运算符,注意:rhs是一个拷贝,和原来的空间无关
	// 而这里拷贝一次,一定会调用拷贝构造函数,计算一定会加1
	smart_ptr& operator=(smart_ptr rhs) noexcept
	{
		rhs.swap(*this);
		return *this;
	}
	// 获得智能指针所管理的指针
	T* get() const noexcept
	{
		return ptr_;
	}
	// 返回指针计数
	long use_count() const noexcept
	{
		if (ptr_) 
		{
			return shared_count_->get_count();
		} 
		else 
		{
			return 0;
		}
	}
	// 调用std::swap,进行指针指针的交换
	void swap(smart_ptr& rhs) noexcept
	{
		using std::swap;
		swap(ptr_, rhs.ptr_);
		swap(shared_count_, rhs.shared_count_);
	}
	// 重载*运算符
	T& operator*() const noexcept
	{
		return *ptr_;
	}
	// 重载->运算符
	T* operator->() const noexcept
	{
		return ptr_;
	}
	operator bool() const noexcept
	{
		return ptr_;
	}
private:
	// 所管理的原有指针
	T* ptr_;
	// 引用计数
	shared_count* shared_count_;
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值