You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何理解C++23中的std::inout_ptr与std::out_ptr?

std::out_ptr / std::inout_ptr 通俗讲解与示例

核心解决的痛点

我们写C++代码经常会调用C风格接口,这类接口有个非常典型的设计:用二级指针作为输出/输入输出参数,返回动态分配的内存或者句柄,比如:

// 示例C接口:分配Widget对象,通过out参数返回指针,返回值为错误码
int c_alloc_widget(size_t count, Widget** out);
// 示例C接口:重分配Widget对象,会读取原有指针做释放/重分配,再写入新指针
int c_realloc_widget(Widget** inout, size_t new_count);

在C++23之前,如果我们要把返回的裸指针直接交给智能指针管理,需要多写一个临时裸指针变量,不仅啰嗦,还可能因为中途异常、提前返回等问题导致内存泄漏:

// C++23之前的写法
Widget* raw = nullptr;
int err = c_alloc_widget(10, &raw);
if (err != 0) {
    // 错误处理,这里如果忘记free就会泄露
    return err;
}
// 手动转移给智能指针
std::unique_ptr<Widget, decltype(&free)> ptr(raw, free);

std::out_ptr和std::inout_ptr就是为了消除这个冗余流程而生的包装器。

二者的区别与用法

std::out_ptr :纯输出参数场景

适用于C接口只会写入二级指针指向的内容、不会读取原有值的场景,直接把智能指针包在std::out_ptr里传给接口即可,不需要临时变量:

std::unique_ptr<Widget, decltype(&free)> ptr(nullptr, free);
int err = c_alloc_widget(10, std::out_ptr(ptr));
if (err == 0) {
    // 直接用ptr即可,内存已经自动接管
}

实现逻辑:std::out_ptr返回一个临时的std::out_ptr_t对象,内部会先重置你传入的智能指针(释放原有资源),再把自己内部持有的临时裸指针的地址传给C接口。等表达式执行完毕、临时对象析构时,会自动把C接口写入的新指针赋值给你传入的智能指针,完成资源接管。

std::inout_ptr:读写参数场景

适用于C接口会先读取二级指针指向的原有值(比如拿去释放、重分配),再写入新值的场景,用法和out_ptr一致:

// 此时ptr已经持有10个Widget的资源
int err = c_realloc_widget(std::inout_ptr(ptr), 20);
if (err == 0) {
    // ptr现在持有20个Widget的资源,原有资源已经被C接口正确处理
}

实现逻辑:和out_ptr不同,std::inout_ptr构造时不会重置智能指针,而是先把智能指针持有的原有裸指针赋值给内部的临时变量,再把临时变量的地址传给C接口。接口执行完毕后,临时对象析构时再把C接口修改后的新指针赋值回智能指针,不会重复释放原有资源。

关于_t后缀类型

std::out_ptr_t和std::inout_ptr_t是两个工厂函数std::out_ptr、std::inout_ptr的返回值类型。普通业务开发不需要手动声明这类变量,直接用工厂函数生成临时对象即可。只有当你需要适配自定义智能指针时,才需要针对这两个类型做自定义特化。

注意事项

  • 两个包装器返回的临时对象只能在当前调用表达式中使用,不能存到变量里后续使用,否则会触发悬垂引用问题。
  • 支持所有符合标准的智能指针(std::unique_ptr、std::shared_ptr等),自定义智能指针只要实现了reset()、get()接口就可以直接适配。
  • 会保留智能指针原有的自定义删除器,不需要额外配置。

完整示例

#include <memory>
#include <cstdlib>
#include <iostream>

// 模拟C风格分配接口
extern "C" int alloc_int(int init_val, int** out) {
    *out = (int*)malloc(sizeof(int));
    if (!*out) return -1;
    **out = init_val;
    return 0;
}

// 模拟C风格重分配接口
extern "C" int realloc_int(int** inout, int new_val) {
    free(*inout);
    *inout = (int*)malloc(sizeof(int));
    if (!*inout) return -1;
    **inout = new_val;
    return 0;
}

int main() {
    std::unique_ptr<int, decltype(&free)> p(nullptr, free);
    
    // out_ptr 示例
    int err = alloc_int(42, std::out_ptr(p));
    if (err == 0) {
        std::cout << *p << std::endl; // 输出 42
    }

    // inout_ptr 示例
    err = realloc_int(std::inout_ptr(p), 100);
    if (err == 0) {
        std::cout << *p << std::endl; // 输出 100
    }

    return 0;
}

内容的提问来源于stack exchange,提问作者cbuchart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 05:09:00