c++智能指针 unique_ptr与函数调用

unique_ptr与函数调用

1、unique_ptr是不可copy,只可以Move

2、在做函数参数或是返回值中一定要注意所有权

函数调用与unique_ptr注意事项

Passing by value 独占指针作为传参的值

复制代码
· 需要用std::move 来转移内存拥有权,不调用会编译报错
· 如果参数直接传入std:make_unique语句 自动转为move

Passing by reference 传引用作为参数

复制代码
· 如果设置参数为 const 则不能改变指向,不能够再指向其他的地方
  比方说 reset()
  reset()方法为智能指针清空方法,指向空

Return by value 作为返回值

复制代码
· 指向一个 local object
· 可以用作链式函数

代码实践

Passing by value 独占指针作为传参的值

cpp 复制代码
#include <iostream>

// 智能指针头文件
#include <memory>
// 自定义类头文件
#include "CustomClass.h"

using namespace std;

// 独占指针作为传参的值
void  funcPassByValue(std::unique_ptr<CustomClass> c)
{
    c->cat_info();
}

int main()
{

    std::unique_ptr<CustomClass> upc1 = make_unique<CustomClass>("upc1");

    // 1 Pass value
    funcPassByValue(std::move(upc1));

    std::cout << "----------- END --------------------\n";
    return 0;
}

打印结果:

可以看到cat_info被调用,正常打印,但是此时不能再对move后的指针进行操作,会崩溃

尝试对 move 后的指针进行操作
cpp 复制代码
#include <iostream>

// 智能指针头文件
#include <memory>
// 自定义类头文件
#include "CustomClass.h"

using namespace std;

// 独占指针作为传参的值
void  funcPassByValue(std::unique_ptr<CustomClass> c)
{
    c->cat_info();
}

int main()
{

    std::unique_ptr<CustomClass> upc1 = make_unique<CustomClass>("upc1");

    // 1 Pass value
    funcPassByValue(std::move(upc1));
    
    // move后再调用原指针,程序会崩溃报错
    upc1->cat_info();


    std::cout << "----------- END --------------------\n";
    return 0;
}

打印结果:

在运行程序后,vs执行发生崩溃。从图中可以看到,代码从 upc1->cat_info 这行语句崩溃,报错为空指针,说明upc1指针已经被转移,原先的指针为空指针了

若直接在参数中进行make,会自动转成move
cpp 复制代码
#include <iostream>

// 智能指针头文件
#include <memory>
// 自定义类头文件
#include "CustomClass.h"

using namespace std;

// 独占指针作为传参的值
void  funcPassByValue(std::unique_ptr<CustomClass> c)
{
    c->cat_info();
}

int main()
{
    std::unique_ptr<CustomClass> upc1 = make_unique<CustomClass>("upc1");

    // 1 Pass value
    funcPassByValue(std::move(upc1));

    // move后再调用原指针,程序会崩溃报错
    // upc1->cat_info();

    funcPassByValue(make_unique<CustomClass>()); // 自动转成move

    std::cout << "----------- END --------------------\n";
    return 0;
}

打印结果:

可以看到 函数中的 cat_info 正常调用,说明 unique_ptr 的所有权被 move 放进了函数中

Passing by reference 传引用作为参数

cpp 复制代码
#include <iostream>

// 智能指针头文件
#include <memory>
// 自定义类头文件
#include "CustomClass.h"

using namespace std;

// 引用作为参数 不加const
void funcPassByRefNoConst(std::unique_ptr<CustomClass> &c)
{
    c->set_name("funcPassByRefNoConst");
    c->cat_info();
    
    // 清空指针
    c.reset();
}

// 引用作为参数 加const
void funcPassByRefWithConst(const std::unique_ptr<CustomClass> &c)
{
    c->set_name("funcPassByRefWithConst");
    c->cat_info();

    // 清空指针,加const之后就不能再进行reset了
    // c.reset();
}

// 返回值为unique_ptr
std::unique_ptr<CustomClass> funcReturnUniquePtr()
{
    std::unique_ptr<CustomClass> upc = std::make_unique<CustomClass>("return unique_ptr");
    std::cout << "return unique_ptr address:" << upc.get() << std::endl;
    std::cout << "return unique_ptr address:" << &upc << std::endl;
    return upc;
}

int main()
{
    // 2 Pass ref
    // 不加const
    std::unique_ptr<CustomClass> upc2 = make_unique<CustomClass>("upc2");
    funcPassByRefNoConst(upc2);
    // 指针被清空,调用会崩溃    
    // upc2->cat_info();
    std::cout << "upc2 address:" << upc2.get() << std::endl;

    // 加const
    std::unique_ptr<CustomClass> upc3 = make_unique<CustomClass>("upc3");
    funcPassByRefWithConst(upc3);
    upc3->cat_info();
    std::cout << "upc3 address:" << upc3.get() << std::endl;

    std::cout << "----------- END --------------------\n";
    return 0;
}

打印结果:

通过打印结果得到如下结论

1、可以看到如果指针被 reset 后,指向地址为0,无法再进行get/set 操作。如果不想指针在函数中被修改指向,需要在参数加入 const 修饰,禁止修改指向

2、而且通过打印结果可以看出,Passing by reference 与 Passing by value 是不一样的,Passing by reference 不会修改unique_ptr 的所有权,在函数执行后仍可进行操作

Return by value 作为返回值

cpp 复制代码
#include <iostream>

// 智能指针头文件
#include <memory>
// 自定义类头文件
#include "CustomClass.h"

using namespace std;

// 返回值为unique_ptr
std::unique_ptr<CustomClass> funcReturnUniquePtr()
{
    std::unique_ptr<CustomClass> upc = std::make_unique<CustomClass>("return unique_ptr");
    std::cout << "return unique_ptr address:" << upc.get() << std::endl;
    std::cout << "return unique_ptr address:" << &upc << std::endl;
    return upc;
}

int main()
{
    // 3 rerutn 
    //链式
    funcReturnUniquePtr()->cat_info();

    std::cout << "----------- END --------------------\n";
    return 0;
}

打印结果:

总结

1、unique_ptr 在当作参数传入函数时,只能使用 move 转移所有权,转移所有权后旧指针会被置空

2、unique_ptr 使用引用作为参数时,不需要考虑所有权的问题,但是如果函数中指针指向出现变化(如 reset)可能会导致问题,若不希望指针被修改,需要增加const修饰

3、推荐使用引用传值,使用方便,且问题相对可控

相关推荐
Leo.yuan2 小时前
Kafka+Flink拼凑,还是FineDataLink 5.0一体化?实时数据处理的两种思路
c++·mfc
INGNIGHT3 小时前
944 · 最大子矩阵(前缀和)
数据结构·c++·算法
All for pursuit.4 小时前
【动态规划-6】96.不同的二叉搜索树
数据结构·c++·算法·leetcode·动态规划
大鹏的NLP博客5 小时前
C++ 内存安全的未来:从 Safety Profiles 到 Safe C++
c++·内存安全
程序员zgh5 小时前
C++ erase() 函数用法
c语言·开发语言·c++·学习
All for pursuit.6 小时前
【并查集-2】399.除法求值
数据结构·c++·算法·leetcode
程序喵大人7 小时前
【C++入门】编译链接模型 - 05 ODR 为什么会让重复定义变成工程炸点
开发语言·c++·编译链接·odr
汉克老师8 小时前
CSP-S 2026 初赛试题解析(第一部分:选择题(8-15))精讲
c++·csp-s·小学生·学c++编程
_wyt0018 小时前
洛谷 P10112 [GESP202312 八级] 奖品分配 题解
c++·数学·gesp·八级