智能指针unique_ptr

unique_ptr通过指针占有并管理另一个对象(主要负责管理对象的销毁,或者说生命周期)。当unique_ptr被销毁时或者给unique_ptr赋值另一个对象的指针时,unique_ptr将删除原来管理的对象。unique_ptr可以管理单个对象,也可以管理动态分配的对象数组。unique_ptr类满足可移动构造以及可移动赋值,但是不满足可复制构造和可复制赋值的要求。

代码库:

https://gitee.com/gamestorm577/CppStd

创建unique_ptr

一般通过std::make_unique来创建一个unique_ptr。

例如创建一个管理单个类对象的unique_ptr:

struct MyStruct
{
    MyStruct()
    {
        printf("MyStruct1, {X: %d, Y: %d}\n", X, Y);
    }
    MyStruct(int x, int y)
        : X(x)
        , Y(y)
    {
        printf("MyStruct2, {X: %d, Y: %d}\n", X, Y);
    }
    int X = 0;
    int Y = 0;
};

std::cout << "construct a unique_ptr of single object: " << std::endl;
std::unique_ptr<MyStruct> ptr1 = std::make_unique<MyStruct>();

std::cout << "construct a unique_ptr of single object: " << std::endl;
std::unique_ptr<MyStruct> ptr2 = std::make_unique<MyStruct>(3, 5);

终端输出结果为:

construct a unique_ptr of single object: 
MyStruct1, {X: 0, Y: 0}
construct a unique_ptr of single object: 
MyStruct2, {X: 3, Y: 5}

也可以创建一个管理动态分配的对象数组的unique_ptr:

std::cout << "construct a unique_ptr of object array: " << std::endl;
std::unique_ptr<MyStruct[]> ptr3 = std::make_unique<MyStruct[]>(3);

终端输出结果为:

construct a unique_ptr of object array: 
MyStruct1, {X: 0, Y: 0}
MyStruct1, {X: 0, Y: 0}
MyStruct1, {X: 0, Y: 0}

对象生命周期管理

智能指针的主要功能是管理对象的生命周期。对于一个动态分配的对象,当我们不需要时,需要手动删除这个对象,这种手动管理的方式非常容易犯错。unique_ptr是一个栈上的变量,它的内存申请和回收都是系统自动完成的。通过unique_ptr来管理动态分配的对象的生命周期,当unique_ptr被销毁时,自动删除其占用的对象,这样更智能,不容易犯错。

例如,当unique_ptr离开作用域时,unique_ptr被销毁,unique_ptr管理的对象被自动删除:

struct MyStruct
{
    ~MyStruct()
    {
        printf("~MyStruct\n");
    }
};

{
    std::unique_ptr<MyStruct> ptr = std::make_unique<MyStruct>();
    printf("About to leave scope\n");
}
printf("Enter new scope\n");

终端输出结果为:

About to leave scope
~MyStruct
Enter new scope

unique_ptr也是异常安全的,当unique_ptr离开作用域前抛出异常时,也能正常删除占有的对象:

struct MyStruct
{
    ~MyStruct()
    {
        printf("~MyStruct\n");
    }
};

try
{
    std::unique_ptr<MyStruct> ptr = std::make_unique<MyStruct>();
    printf("About to throw\n");
    throw std::exception();
    printf("After to throw\n");
}
catch (std::exception e)
{
    printf("Catch\n");
}

终端输出结果为:

About to throw
~MyStruct
Catch

在ptr离开作用域前就抛出了异常,但是在进入catch之前Mystruct的析构函数还是被正常调用了 

自定义Deleter

unique_ptr也可以自定义Deleter

struct MyStruct
{
    ~MyStruct()
    {
        printf("~MyStruct\n");
    }
};

struct Deleter
{
    void operator()(MyStruct* p) const
    {
        printf("Deleter\n");
        delete p;
    }
};

{
    printf("ptr1: \n");
    std::unique_ptr<MyStruct> ptr1 = std::make_unique<MyStruct>();
}

{
    printf("ptr2: \n");
    std::unique_ptr<MyStruct, Deleter> ptr2(new MyStruct);
}

终端输出结果为:

ptr1: 
~MyStruct
ptr2: 
Deleter
~MyStruct

访问对象

当unique_ptr管理的是单个对象时,可以使用重载操作符 operator* 来获取管理的对象,或者通过重载操作符 operator-> 来直接访问对象的成员:

struct MyStruct
{
    void Test()
    {
        printf("MyStruct::Test\n");
    }
};

printf("single object\n");
std::unique_ptr<MyStruct> ptr1 = std::make_unique<MyStruct>();
MyStruct& s1 = *ptr1;
s1.Test();
ptr1->Test();

输出结果为:

single object
MyStruct::Test
MyStruct::Test

当unique_ptr管理的是对象数组时,可以通过重载操作符 operator[] 来获取管理的对象:

printf("object array\n");
std::unique_ptr<MyStruct[]> ptr2 = std::make_unique<MyStruct[]>(5);
for (int i = 0; i < 5; ++i)
{
    MyStruct& s = ptr2[i];
    s.Test();
}

输出结果为:

object array
MyStruct::Test
MyStruct::Test
MyStruct::Test
MyStruct::Test
MyStruct::Test

其他接口

移动赋值函数

通过移动赋值函数,可以将对象的所有权从一个unique_ptr移交给另一个unique_ptr。对象的生命周期也从跟随原unique_ptr到跟随目标unique_ptr。例如:

struct MyStruct
{
    MyStruct(int id)
        : ID(id)
    {
        printf("MyStruct, id = %d\n", ID);
    }
    ~MyStruct()
    {
        printf("~MyStruct, id = %d\n", ID);
    }
    int ID = 0;
};

{
    printf("test1: \n");
    printf("Enter outer scope\n");
    std::unique_ptr<MyStruct> ptr1 = nullptr;
    {
        printf("Enter inner scope\n");
        std::unique_ptr<MyStruct> ptr2 = std::make_unique<MyStruct>(1);
        ptr1 = std::move(ptr2);
        printf("Leave inner scope\n");
    }
    printf("Leave outer scope\n");
}

输出结果为:

test1: 
Enter outer scope
Enter inner scope
MyStruct, id = 1
Leave inner scope
Leave outer scope
~MyStruct, id = 1

ptr2将对象的所有权移交给ptr1,当ptr2的作用域结束时,对象并没有被删除,而只有当ptr1的作用域结束时,对象才被删除。

当被移交的目标unique_ptr不为空时,目标unique_ptr原有的对象会被删除。例如:

{
    printf("test2: \n");
    std::unique_ptr<MyStruct> ptr1 = std::make_unique<MyStruct>(1);
    std::unique_ptr<MyStruct> ptr2 = std::make_unique<MyStruct>(5);
    printf("start move ptr2 to ptr1\n");
    ptr1 = std::move(ptr2);
    printf("end move ptr2 to ptr1\n");
}

输出结果为:

MyStruct, id = 1
MyStruct, id = 5
start move ptr2 to ptr1
~MyStruct, id = 1
end move ptr2 to ptr1
~MyStruct, id = 5

当ptr2将对象移交给ptr1时,ptr1的作用域并没有结束,但是ptr1只能占有一个对象,所以原来占有的对象将被删除。

get

返回管理对象的指针,例如:

std::unique_ptr<double> ptr = std::make_unique<double>(20.5);
double* val = ptr.get();
printf("number = %.3f\n", *val);

输出结果为:

number = 20.500
Program ended with exit code: 0

get_deleter

获取unique_ptr的deleter,例如:

struct MyStruct
{
};

struct Deleter
{
    void Test()
    {
        printf("Deleter::Test\n");
    }

    void operator()(MyStruct* p) const
    {
        delete p;
    }
};

std::unique_ptr<MyStruct, Deleter> ptr(new MyStruct);
auto& del = ptr.get_deleter();
del.Test();

输出结果为:

Deleter::Test

operator bool

判断unique_ptr是否占有对象,例如:

auto Func = [](const std::unique_ptr<int>& ptr) -> void
{
    if (ptr)
    {
        printf("ptr not empty\n");
    }
    else
    {
        printf("ptr empty\n");
    }
};

std::unique_ptr<int> ptr1 = std::make_unique<int>();
std::unique_ptr<int> ptr2 = nullptr;
Func(ptr1);
Func(ptr2);

输出结果为:

ptr not empty
ptr empty

release

unique_ptr不再占用管理的对象并返回该对象的指针,如果管理的是空对象则返回nullptr,例如:

struct MyStruct
{
    ~MyStruct()
    {
        printf("~MyStruct\n");
    }
};

{
    std::unique_ptr<MyStruct> ptr = std::make_unique<MyStruct>();
    MyStruct* tmp = ptr.release();
    printf("About to enter outer scope\n");
}
printf("Enter outer scope\n");

输出结果为:

About to enter outer scope
Enter outer scope

调用release接口后,ptr不再占用对象,也不再负责对象的生命周期,所以当ptr离开作用域时,并没有删除对象。

reset

删除旧的对象,替换新的对象,例如:

struct MyStruct
{
    MyStruct(int id)
        : ID(id)
    {
    }

    ~MyStruct()
    {
        printf("~MyStruct, id = %d\n", ID);
    }

    int ID = 0;
};

MyStruct* tmp = new MyStruct(20);
std::unique_ptr<MyStruct> ptr = std::make_unique<MyStruct>(5);
ptr.reset(tmp);
printf("finish reset\n");

输出结果为:

~MyStruct, id = 5
finish reset
~MyStruct, id = 20
Program ended with exit code: 0

swap

交换两个unique_ptr管理的对象,例如:

struct MyStruct
{
    MyStruct(int id)
        : ID(id)
    {
    }

    void Test()
    {
        printf("Test, id = %d\n", ID);
    }

    int ID = 0;
};

std::unique_ptr<MyStruct> ptr1 = std::make_unique<MyStruct>(5);
std::unique_ptr<MyStruct> ptr2 = std::make_unique<MyStruct>(20);
ptr1->Test();
ptr2->Test();
ptr1.swap(ptr2);
ptr1->Test();
ptr2->Test();

输出结果为:

Test, id = 5
Test, id = 20
Test, id = 20
Test, id = 5

operator<<

输出unique_ptr管理对象的指针的值到输出流中,例如:

std::unique_ptr<int> ptr = std::make_unique<int>(5);
std::cout << ptr << std::endl;
0x600000008040

unique_ptr的比较

==、!=、<、<=、>、>=、<=>等符号可用于两个unique_ptr或者unique_ptr与nullptr的比较,比较的值是unique_ptr管理对象的指针的值,例如:

std::unique_ptr<int> ptr1 = std::make_unique<int>(5);
std::unique_ptr<int> ptr2 = std::make_unique<int>(5);
std::cout << "ptr1 == ptr1: " << (ptr1 == ptr1) << std::endl;
std::cout << "ptr1 == ptr2: " << (ptr1 == ptr2) << std::endl;

hash

计算对象的散列值,例如:

struct MyStruct
{
};

MyStruct* tmp = new MyStruct;
std::unique_ptr<MyStruct> ptr(tmp);
std::cout << std::hash<std::unique_ptr<MyStruct>>()(ptr) << std::endl;
std::cout << std::hash<MyStruct*>()(tmp) << std::endl;

输出结果为:

12022488372033534701
12022488372033534701
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值