修改很简单,但网上讲这点的文档不多,因此多记一笔。另外基于out_ptr会临时转移所有权这点来看,共享所有权模型的std::shared_ptr其实并不适合使用out_ptr,虽然标准没有禁止甚至还要

发布时间:2026/7/1 2:19:50
修改很简单,但网上讲这点的文档不多,因此多记一笔。另外基于out_ptr会临时转移所有权这点来看,共享所有权模型的std::shared_ptr其实并不适合使用out_ptr,虽然标准没有禁止甚至还要 inout_ptr的名字比较抽象但只是在out_ptr的基础上加了个“in”而已。它会返回一个std::inout_ptr_t类型的对象函数签名如下#include memorytemplate class Pointer void, class Smart, class... Args auto inout_ptr( Smart s, Args... args );这个“in”是指使用output parameter的函数在重新设置参数的值之前会先使用他们因此这些函数的特点是非常在乎output parameter里有什么值根据这些值执行不同的操作函数调用期间完全享有output parameter和其资源的所有权函数返回后output parameter不变或者被设置为新值还是看例子我们对Data增加一个update_data函数如果name是recreate则删除原来的对象重新创建一个int update_data(Data **data){if (data nullptr || *data nullptr)return 1;if ((*data)-name recreate) {delete *data;*data new Data{apocelipes};return 2; // 代表已修改}return 0;}现实中没人这么写代码但存在很多类似的c接口而且我们也很难控制第三方库的代码质量难免不会遇上类似的东西。如果想在这种接口上用智能指针那只能说有福了auto resource std::make_uniqueData(recreate);Data *ptr resource.get();resource.release(); // 释放所有权但不释放资源if (auto code update_data(ptr); code 1)std::cerr error\n;else if (code 2) {resource.reset(ptr);std::cout updated, name: resource-name \n;} else {resource.reset(ptr);std::cout updated, name: resource-name \n;}可以看到代码会变得很复杂而且一但忘记使用reset就会内存错误。这时候我们就需要inout_ptr帮忙了。inout_ptr整体上和out_ptr差不多都是让出资源的所有权然后重新把函数返回的值设置回去但还有几个差异前面说过需要inout_ptr的函数是需要参数的值的因此构造inout_ptr_t时之后放弃资源的所有权不会像out_ptr那样释放资源本身资源的释放是调用的函数的责任inout_ptr只会把函数返回出来的值重新设置回智能指针用inout_ptr改写后的代码如下auto resource std::make_uniqueData(recreate);if (auto code update_data(std::inout_ptr(resource)); code 1)std::cerr error\n;else if (code 2) {std::cout updated, name: resource-name \n;} else {std::cout updated, name: resource-name \n;}代码看起来清爽多了。另外虽然inout_ptr也有变长参数但标准明确规定它不能配合std::shared_ptr使用这些参数std::unique_ptr用不上是预留给其他的第三方的类似指针对象使用的。注意事项除了std::shared_ptr配合out_ptr使用时需要传入deleter还有一个注意事项。两个适配器都不建议这么用auto out std::out_ptr(resource);func(out);因为他们都是在析构函数里重新设置智能指针的值如果绑定到一个局部变量或者其他存储器的变量上函数调用结束就无法把正确的值重新设置回智能指针这会导致严重的内存错误。唯一建议的用法是直接使用out_ptr和inout_ptr的返回值func(std::out_ptr(resource))这样函数调用结束后表达式结束返回值作为表达式中创建的临时变量会被析构这样智能指针的值就被正常设置了。尽管只要在转换操作符上加上一点限制就能避免误用但标准考虑到了各种边缘情形最终没有添加限制所以我们只能牢记这条注意事项避免踩坑了。