C++ / a working model

36 / 80   ·   C++14   ·   约 9 分钟

参数传递 Parameter passing 与所有权契约

先记住这句话

选参数类型时先决定函数只是读取、需要修改,还是接收所有权,再比较复制成本。小值按值,大型只读对象用 const 引用,可空借用用指针,独占所有权按值传 unique_ptr;这些类型传达的是不同契约。

本篇内容
  1. 传值不是一律低效
  2. 借用与接管要在签名里区分
  3. 保留数据时选择拥有型表示
  4. 运行示例
  5. 动手练习

传值不是一律低效

按值参数是独立的参数对象,修改它通常不会修改调用者的那个对象。整数和轻量句柄按值传递简单清晰,是否更快需要结合类型与目标平台。按 const 引用传大型只读对象可以避免复制,但增加别名关系,并要求调用期间目标一直有效。

复制一个指针只复制指针值:函数可以通过副本修改同一个目标,却不能靠给这个副本重新赋地址来替换调用者的指针。若确实要重设外部指针,可以传指针引用;更多时候直接返回新结果,接口更容易理解。

借用与接管要在签名里区分

T& 适合必需的可修改借用,const T& 适合必需的只读借用,T* 可表达可选目标。它们都不应默认为转移所有权。函数如果需要销毁或长期保存独占资源,应按值接收 std::unique_ptr<T>,调用方明确使用 move 交接责任。

只读取 T 的函数没有必要接收 shared_ptr。把智能指针参数留给需要操作所有权的接口,可以避免不必要的引用计数更新,也让使用自动对象或 unique_ptr 的调用者无需迁就某种存储策略。

保留数据时选择拥有型表示

示例中的 decorate 按值接收字符串,因为它需要本地可修改副本,并按值返回结果;调用者的原字符串不变。consume 接收 unique_ptr,返回整数后由参数析构回收资源。调用前后的断言明确显示独占所有权已被转移。

需要把文本保存到对象中时,按值接收 string 再移动进成员是常见做法;传入左值会复制,传入临时值可以移动。string_view 与 span 的按值传递只复制视图,不复制底层数据,不能因此把它们直接保存到超过输入生存期的异步任务中。

容易答错的地方

  • std::move 只是允许选择移动操作的转换,不保证任意类型都执行移动,也不会单独释放资源。
  • const 引用参数不能自动使函数保存的引用长期有效;调用结束后继续借用必须另有生存期保证。

运行一个例子

最低标准 C++14 · 完整程序 · 下载 .cpp

#include <cassert>
#include <memory>
#include <string>
#include <utility>

std::string decorate(std::string text) {
    text += '!';
    return text;
}

int consume(std::unique_ptr<int> value) {
    assert(value);
    return *value;
}

int main() {
    std::string original = "C++";
    assert(decorate(original) == "C++!");
    assert(original == "C++");
    auto owner = std::make_unique<int>(42);
    int result = consume(std::move(owner));
    assert(result == 42);
    assert(!owner);
}

在本地编译

g++ -std=c++14 -Wall -Wextra -Wpedantic -pthread memory-parameters.cpp -o example && ./example

预期结果

预期:正常退出、无输出;所有 assert 通过。

CHECK YOUR UNDERSTANDING

合上答案,试着解释。

设计一个只在调用期间读取 Record、允许没有 Record 的函数参数;它需要 shared_ptr 吗?

查看参考答案

可以声明为 void inspect(const Record* record),在函数内先检查 record 是否为空,再只读访问。不需要 shared_ptr,因为没有保存或共享所有权。若目标必需,改成 const Record&;若要排入稍后执行的任务,则应改成拥有值副本,或明确按值接收 shared_ptr 以延长生存期。

继续查证

标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。

回到目录