[C++]智能指针(一)——auto_ptr和unique_ptr及其实现

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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值