C++11:unique_ptr 自己定义类似make_shared的make_unique模板函数

本文介绍了C++14中make_unique函数的实现方法,包括如何创建普通对象和数组对象的智能指针unique_ptr,并对比了不同版本的实现差异。

C++11中的智能指针分为共享型的shared_ptr和独占型的unique_ptr,C++11提供了make_shared函数来创建shared_ptr指针,使用起来更方便,有了make_shared函数,就可以完全摆脱new操作了,可以写出完全没有new/delete的程序。
但是unique_ptr却不同,unique_ptr不像shared_ptr可以通过make_shared方法来创建智能指针,C++11目前还没有提供make_unique函数,在C++14中才会提供make_shared方法类似的make_unique来创建unique_ptr.

make_unique实现

其实要实现make_unique函数并不复杂,创建普通对象指针的代码如下:

#include <type_traits>
#include <memory>
// 支持普通指针
template<typename T,typename ...Args>inline
unique_ptr<T>
make_unique(Args&&...args){
	static_assert(!is_array<T>::value,"T must not be array");
	return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

调用方式

class test_class{
	int a;
	int b;
	public:
	test():a(),b(){}
	test(int a1,int b1):a(a1),b(b1){}
};
auto obj=make_unique<test_class>(2,3);
auto obj1=make_unique<test_class>();

创建数组指针的unique_ptr代码如下:

#include <type_traits>
#include <memory>
// 初始化版本
template<typename T,bool ZERO=true>inline
typename enable_if<ZERO,unique_ptr<T>>::type
make_unique_array(size_t size){
	// T必须是动态数组类型,且不能是定长数组
	static_assert(is_array<T>::value&& extent<T>::value==0,"T must be dynamic array");
	using U=typename remove_extent<T>::type;
	return unique_ptr<T>(new U[size]());
}
//不初始化的版本
template<typename T,bool ZERO=true>inline
typename enable_if<!ZERO,unique_ptr<T>>::type
make_unique_array(size_t size){
	// T必须是动态数组类型,且不能是定长数组
	static_assert(is_array<T>::value&& extent<T>::value==0,"T must be dynamic array");
	using U=typename remove_extent<T>::type;
	return unique_ptr<T>(new U[size]);
}

为了在创建数组时可以选择是否将数组初始化为0,函数分成执行初始化和不初始化的两个版本。
模板参数中增加了一个常量参数ZERO,用于编译期判断。
用到了名为std::enable_if的type_traits,它类似一个if语句,判断ZERO,当ZERO为true时编译器选择第一个版本的函数,反之选择第二个。
enable_if是C++11头文件<type_traits>中的一个类,关于enable_if的用法详细说明参见:

class template <type_traits>std::enable_if

这样以来,虽然代码多了一倍,但是在编译期就选择了不同版本的make_unique_array函数,避免了运行时判断。

调用方式之前的版本差不多,只是将bool参数移到了模板参数<>

	auto test_array=make_unique_array<test_class[],true>(2);

问题来了

以上的办法虽然好,但是却与C++14版本的make_unique在模板参数类型上并不兼容,你为啥知道C++14的make_unique版本是什么样呢?其实我是写完上面的代码在VS2015下编译时,报了个错,
make_unique
我这才发现,VS2015已经提供了make_unique
以下是来自VS2015的<memory>头文件中make_unique的实现代码,代码中创建普通对象和数组对象的函数名都是make_unique,与我写的版本不一样,而且微软的版本中也没有区分是否在初始化数组,一律初始化为0。

	// TEMPLATE FUNCTION make_unique
template<class _Ty,
	class... _Types> inline
	typename enable_if<!is_array<_Ty>::value,
		unique_ptr<_Ty> >::type make_unique(_Types&&... _Args)
	{	// make a unique_ptr
	return (unique_ptr<_Ty>(new _Ty(_STD forward<_Types>(_Args)...)));
	}

template<class _Ty> inline
	typename enable_if<is_array<_Ty>::value && extent<_Ty>::value == 0,
		unique_ptr<_Ty> >::type make_unique(size_t _Size)
	{	// make a unique_ptr
	typedef typename remove_extent<_Ty>::type _Elem;
	return (unique_ptr<_Ty>(new _Elem[_Size]()));//作者注:默认数组初始化为0
	}

template<class _Ty,
	class... _Types>
	typename enable_if<extent<_Ty>::value != 0,
		void>::type make_unique(_Types&&...) = delete;

对这么简单的函数VS2015不可能写一个与标准不兼容的,所以如果考虑到与未来的C++14的兼容性,应该使用这个版本。

参照msvc版本代码修改如下:

#if !defined(_MSC_VER)&&__cplusplus<=201103L
namespace std{
// 支持普通指针
template<typename T,typename ...Args>inline
typename enable_if<!is_array<T>::value,	unique_ptr<T> >::type
make_unique(Args&&...args){
	return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// T必须是动态数组类型,且不能是定长数组
template<typename T>inline
typename enable_if<is_array<T>::value&& extent<T>::value==0,unique_ptr<T>>::type
make_unique(size_t size){
	using U=typename remove_extent<T>::type;
	return unique_ptr<T>(new U[size]());
}
template<typename T,typename ...Args>
typename enable_if<extent<T>::value != 0,	void>::type
make_unique(Args&&...)=delete;
}
#endif /* !defined(_MSC_VER)&&__cplusplus<=201103L */
### C++ 中 `std::unique_ptr` 和 `std::shared_ptr` 的作用及用法 #### 1. `std::unique_ptr` `std::unique_ptr` 是一种独占所有权的智能指针,它确保同一时间只有一个 `std::unique_ptr` 实例拥有某个对象的所有权[^2]。这意味着一旦另一个 `std::unique_ptr` 接管该对象的所有权,原来的 `std::unique_ptr` 将不再持有任何资源。 ##### 特点 - **唯一性**:`std::unique_ptr` 不允许复制操作,仅可以通过移动语义来转移所有权。 - **自动释放内存**:当 `std::unique_ptr` 超出其作用域时,其所管理的对象会被自动销毁并释放内存。 - **轻量级**:由于不涉及引用计数机制,`std::unique_ptr` 的开销较小,通常只有指针本身的大小[^2]。 ##### 使用方法 下面是一个简单的例子展示如何创建和使用 `std::unique_ptr`: ```cpp #include <iostream> #include <memory> int main() { // 创建 unique_ptr 并初始化为 new int(10) std::unique_ptr<int> uptr = std::make_unique<int>(10); // 访问 managed 对象的内容 std::cout << *uptr << std::endl; // 移动 ownership 到另一个 unique_ptr std::unique_ptr<int> uptr2 = std::move(uptr); // 此时 uptr 已经为空,访问会导致未定义行为 if (!uptr) { std::cout << "uptr is empty." << std::endl; } return 0; } ``` --- #### 2. `std::shared_ptr` `std::shared_ptr` 是一种共享所有权的智能指针,允许多个 `std::shared_ptr` 同时管理同一个对象[^3]。通过内部维护的一个引用计数器,`std::shared_ptr` 可以跟踪有多少个实例正在共同管理这个对象。当最后一个 `std::shared_ptr` 被销毁或者放弃对该对象的所有权时,才会真正释放该对象的内存。 ##### 特点 - **共享所有权**:多个 `std::shared_ptr` 可以同时指向同一个动态分配的对象。 - **引用计数**:每当一个新的 `std::shared_ptr` 复制已有的 `std::shared_ptr` 或者被赋值给其他 `std::shared_ptr` 时,引用计数加一;反之减一。 - **线程安全**:引用计数的操作是原子性的,在多线程环境下可以安全地增减引用计数。 ##### 使用方法 以下代码展示了如何创建和使用 `std::shared_ptr`: ```cpp #include <iostream> #include <memory> class MyClass { public: MyClass(int value) : m_value(value) {} void display() const { std::cout << "Value: " << m_value << std::endl; } private: int m_value; }; int main() { // 创建 shared_ptr 并初始化为 new MyClass(42) std::shared_ptr<MyClass> sptr1 = std::make_shared<MyClass>(42); // 输出当前引用计数值 std::cout << "Use count after creating sptr1: " << sptr1.use_count() << std::endl; // 创建第二个 shared_ptr 拷贝第一个 std::shared_ptr<MyClass> sptr2 = sptr1; // 输出新的引用计数值 std::cout << "Use count after copying to sptr2: " << sptr1.use_count() << std::endl; // 调用成员函数 sptr2->display(); return 0; } ``` --- #### 主要区别 | 属性 | `std::unique_ptr` | `std::shared_ptr` | |---------------------|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------| | 所有权 | 唯一所有权 | 共享所有权 | | 引用计数 | 无引用计数(简单高效) | 维护一个引用计数器 | | 性能 | 更高效率,因为不需要额外的空间存储引用计数 | 较低性能,因需维护引用计数 | | 线程安全性 | 非线程安全 | 引用计数部分是线程安全的 | --- 问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值