auto_ptr(C++17中移除)
定义:拥有严格对象所有权语义的智能指针
说明:C++11之前只有auto_ptr,C++11添加了unique_ptr,shared_ptr,weak_ptr来取代auto_ptr,在C++17中,auot_ptr就被删除了
移除原因
-
auto_ptr的拷贝构造和拷贝赋值函数的实现是按照 移动构造 和 移动赋值 函数的语义实现的
-
因为C++11之前没有移动构造和移动赋值,所以C++11之前使用auto_ptr没有任何问题
-
但C++11中添加了移动构造和移动赋值函数,和拷贝构造和拷贝赋值区别开来了,就不能再用拷贝构造去实现移动构造的语义了
构造函数
explicit auto_ptr( X* p = 0 ) throw(); // 参数必须是new出来的动态内存
auto_ptr( auto_ptr& r ) throw(); // 拷贝构造实现的却是移动构造
auto_ptr& operator=(auto_ptr& r); // 拷贝赋值实现的却是移动赋值
拷贝构造
- 拷贝构造之后,原对象把对象的控制权交给新构造的对象,原智能指针就不无法再使用了
auto_ptr<int> p(new int(1024));
auto_ptr<int> p1(p); // p就失效了 把对内存对象的控制权转移给了p1
拷贝赋值
- 拷贝赋值,也意味着对象的控制权转移和释放
auto_ptr<int> p(new int(1024));
auto_ptr<int> p1(new int(9527));
p1 = p; // p就失效了 p1原来控制的动态内存会delete,转而控制原来p控制的动态内存
观察器
// 返回指向被管理对象的指针
T* get() const throw();
// 智能指针在使用时,可以像指针一样使用 解引用 *、->
T& operator*() const throw();
T* operator->() const throw();
修改器
// 释放被管理对象的所有权
T* release() throw(); //返回保有的指针。调用后 *this 保有空指针。
// 替换被管理对象
void reset( T* p = 0 ) throw();
unique_ptr
定义:拥有独有对象所有权语义的智能指针
template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;
//针对数组类型进行了偏特化(局部特化)
template <
class T,
class Deleter
> class unique_ptr<T[], Deleter>;
-
unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作用域时释放该对象的智能指针
-
在下列两者之一发生时用关联的删除器释放对象:
-
销毁了管理的 unique_ptr对象
-
通过 operator=或 reset 赋值另一指针给管理的 unique_ptr 对象。
-
-
unique_ptr删除了拷贝构造和拷贝赋值函数
-
unique_ptr实现了移动构造和移动赋值函数
-
不支持拷贝构造和拷贝赋值,只支持移动构造和移动赋值,所以就确保了unique_ptr的唯一性
unique_ptr的实现
管理单个对象的版本
- 根据unique_ptr的定义,我们可以知道它的成员由一个指针和一个删除器组成,通过指针占有并管理对象,拥有对象的唯一控制权
- 要注意的是在移动赋值和reset 进行赋值时,需要先将自身原本的指针删除,再将被赋值的对象交由当前指针管理
- 以下代码是管理单个对象的版本
#include <iostream>
using namespace std;
// 删除器
template<class T>
class Mydel{
public:
void operator()(T* ptr)const{
cout << "delete ptr" << endl;
delete ptr;
}
};
template<class T,class Deleter=Mydel<T> >
class Unique_ptr{
private:
T* ptr; // 指向动态开辟的内存空间的指针
Deleter del; // 删除器
public:
// 构造函数
Unique_ptr(T* ptr=nullptr):ptr(ptr){
}
// 不支持拷贝构造和拷贝复制
Unique_ptr(const Unique_ptr& p) = delete;
Unique_ptr& operator=(const Unique_ptr& p) = delete;
// 移动构造函数
Unique_ptr(Unique_ptr&& p){
ptr = p.ptr;
del = p.del;
p.ptr = nullptr;
}
// 移动赋值函数
Unique_ptr& operator=(Unique_ptr&& p){
if(this != &p){
if(ptr != nullptr){
del(ptr);
}
ptr = p.ptr;
del = p.del;
p.ptr = nullptr;
}
return *this;
}
// 析构函数
~Unique_ptr(){
if(ptr != nullptr){
del(ptr);
}
}
// 返回一个指向被管理对象的指针,并释放所有权
T* release(){
T* retptr = ptr;
ptr = nullptr;
return retptr;
}
// 返回指向被管理对象的指针
T* get()const{
return ptr;
}
// 替换被管理对象
void reset(T* ptr){
if(this->ptr != nullptr)
del(this->ptr);
this->ptr = ptr;
}
// 解引用指向被管理对象的指针
T& operator*()const{
return *ptr;
}
T* operator->()const{
return ptr;
}
// 检查是否有关联的被管理对象
operator bool(void)const{
return ptr != nullptr;
}
Deleter get_deleter()const{
return del;
}
};
管理动态分配的对象的数组版本
- 数组版本较于单个对象版本的区别主要在于删除器的实现,单个对象版本的释放是通过delete实现的;而数组版本的释放是通过delete[] 来实现的
- 还有多了个重载[]的运算符,以此来支持下标访问
- 以下代码为管理动态分配的对象的数组版本
// 针对数组的偏特化删除器
template<class T>
class Mydel<T[]>{
public:
void operator()(T* ptr)const{
cout << "delete[] ptr" << endl;
delete[] ptr;
}
};
// 针对数组进行偏特化
// 注意:此时偏特化版本的模板中不能有默认值
template<class T,class Deleter>
class Unique_ptr<T[],Deleter>{
private:
T* ptr;
Deleter del;
public:
Unique_ptr(T* ptr=nullptr):ptr(ptr){
}
~Unique_ptr(){
if(ptr != nullptr){
del(ptr);
}
}
// 移动构造函数
Unique_ptr(Unique_ptr&& p){
ptr = p.ptr;
del = p.del;
p.ptr = nullptr;
}
// 移动赋值函数
Unique_ptr& operator=(Unique_ptr&& p){
if(this != &p){
if(ptr != nullptr){
del(ptr);
}
ptr = p.ptr;
del = p.del;
p.ptr = nullptr;
}
return *this;
}
// 重载[] 运算符,提供到被管理数组的有索引访问
T& operator[](size_t n){
return ptr[n];
}
// 返回一个指向被管理对象的指针,并释放所有权
T* release(){
T* retptr = ptr;
ptr = nullptr;
return retptr;
}
// 返回指向被管理对象的指针
T* get()const{
return ptr;
}
// 替换被管理对象
void reset(T* ptr){
if(this->ptr != nullptr)
del(this->ptr);
this->ptr = ptr;
}
// 解引用指向被管理对象的指针
T& operator*()const{
return *ptr;
}
T* operator->()const{
return ptr;
}
// 检查是否有关联的被管理对象
operator bool(void)const{
return ptr != nullptr;
}
Deleter get_deleter()const{
return del;
}
};
测试代码
/*
** 以下为测试类
*/
struct A{
int a;
int b;
public:
A(int a=0,int b=0):a(a),b(b){
cout << "A()" << endl;
}
~A(){
cout << "~A()" << endl;
}
void show(){
cout << a << ":" << b << endl;
}
};
int main(){
try{
Unique_ptr<A> up1(new A(1024,9527));
throw int(1); // 抛出异常,A对象能正常析构
}catch(...){
}
cout << "-------------------" << endl;
Unique_ptr<A> up(new A(1024,9527)); // 调用构造函数
cout << (*up).a << endl; // 1024
cout << (*up).b << endl; // 9527
up->show();
cout << "-------------------" << endl;
Unique_ptr<A> up1(move(up)); // 调用移动构造函数
cout << boolalpha << bool(up) << endl; // false,此时up失去了原来内存的控制权
cout << bool(up1) << endl; // true,up的控制权转移给了up1
cout << "-------------------" << endl;
Unique_ptr<A> up2(new A(111,222));
up2 = move(up1); // 调用移动赋值函数
cout << bool(up1) << endl; // false,此时up1失去了原来内存的控制权,转移给了up2
cout << up1.get() << endl; // 0
cout << up2.get() << endl; // 原up1的地址
cout << "-------------------" << endl;
up2.reset(new A(1,2)); // up2控制的内存替换成了新开辟的A对象的内存
cout << up2.get() << endl;
A *pa = up2.release(); // 返回up2控制的内存地址,并释放up2的控制权
cout << up2.get() << endl; // 0
delete pa;
pa = new A[3];
Unique_ptr<A[]> up4(pa);
for(int i=0;i<3;++i){
up4[i].a = i;
up4[i].b = i;
up4[i].show();
}
return 0;
}