首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++引用专题(下):传值返回 VS 传引用返回

C++引用专题(下):传值返回 VS 传引用返回

作者头像
艾莉丝努力练剑
发布2025-11-13 10:28:27
发布2025-11-13 10:28:27
2050
举报
文章被收录于专栏:C / C++C / C++

🔥个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》C语言刷题12天IO强训LeetCode代码强化刷题C/C++干货分享&学习过程记录 🍉学习方向:C/C++方向 ⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平



前言:本专栏记录了博主C++从初阶到高阶完整的学习历程,会发布一些博主学习的感悟、碰到的问题、重要的知识点,和大家一起探索C++这门程序语言的奥秘。这个专栏将记录博主C++语法、高阶数据结构、STL的学习过程,正所谓“万丈高楼平地起”嘛,我们话不多说,继续进行C++阶段的学习。本文我们不讲C++主线的内容,我们来拓展一下或者说整理一下我们学习时C/C++时经常会提到的一些专有名词,例如形参、实参,显式类型转换和隐式类型转换类型转换,内置类型、引用、传值返回和传引用返回等等。


C++的两个参考文档:

老朋友(非官方文档):cplusplus 官方文档(同步更新):cppreference

正文:对比传值返回和传引用返回

博主之前写了传值返回和传引用返回相关的内容,只不过那是在C++初识部分介绍的,比较简略:

【C/C++】初识C++(二):深入详解缺省参数(默认参数)函数重载、引用(重头戏)

【C/C++】初识C++(三):C++入门内容收尾——const引用,指针和引用关系梳理,inline(内联函数),nullptr替代NULL

一、基本概念对比

特性

传值返回

传引用返回

返回内容

返回对象的副本

返回对象的引用

内存开销

可能有拷贝开销

无额外拷贝开销

修改原对象

不能修改原对象

可以直接修改原对象

适用场景

返回局部变量或需要独立副本的情况

返回成员变量或静态变量等长期存在的对象

安全性

高(不会产生悬空引用)

需注意生命周期(可能产生悬空引用)

二、结合示例

传值返回示例
代码语言:javascript
复制
// 返回局部变量的拷贝(安全)
std::string getString() 
{
    std::string local = "Hello";
    return local;  // 返回拷贝(可能触发NRVO优化)
}

// 返回计算结果的拷贝
int sum(int a, int b) 
{
    return a + b;  // 返回临时值的拷贝
}

void testValueReturn() 
{
    std::string s = getString();
    int result = sum(5, 3);
    
    // 可以安全地存储和使用返回值
    std::vector<std::string> strings;
    strings.push_back(getString());
}
传引用返回示例
代码语言:javascript
复制
// 返回静态变量的引用(安全)
const std::string& getStaticString() {
    static std::string s = "Static";
    return s;  // 安全,因为静态变量生命周期长
}

// 返回成员变量的引用(需注意对象生命周期)
class MyClass {
    std::string name;
public:
    MyClass(const std::string& n) : name(n) {}
    
    // 返回成员引用(安全,只要对象存在)
    const std::string& getName() const { return name; }
    
    // 返回非const引用(允许修改成员)
    std::string& getMutableName() { return name; }
};

// 危险示例:返回局部变量的引用
const std::string& dangerousRef() {
    std::string local = "Danger";
    return local;  // 警告:返回局部变量引用
}

void testRefReturn() {
    // 安全使用
    const std::string& s1 = getStaticString();
    MyClass obj("Test");
    const std::string& name = obj.getName();
    
    // 修改成员变量
    obj.getMutableName() = "Modified";
    
    // 危险使用(未定义行为)
    // const std::string& bad = dangerousRef();
}
右值引用返回 (C++11)
代码语言:javascript
复制
// 工厂函数返回右值
std::vector<int> createVector() {
    return std::vector<int>{1, 2, 3};  // 返回临时对象
}

// 移动语义优化
class BigData {
    std::vector<int> data;
public:
    BigData() : data(1000000) {}  // 大对象
    
    // 返回右值引用(转移所有权)
    BigData&& moveFrom() { return std::move(*this); }
};

void testRvalueReturn() {
    // 高效接收返回值(可能触发移动构造)
    std::vector<int> v = createVector();
    
    BigData b1;
    BigData b2 = b1.moveFrom();  // 移动构造
}

三、最佳实践指南

1、优先传值返回

  • 返回局部变量或临时对象时
  • 当需要独立副本时
  • 现代C++的返回值优化(RVO/NRVO)会消除拷贝

2、谨慎使用传引用返回:

  • 返回成员变量、静态变量或全局变量时
  • 需要允许调用者修改对象时
  • 确保被引用对象的生命周期足够长

3、避免常见错误:

代码语言:javascript
复制
// 错误1:返回局部变量引用
const int& badRef1() 
{
    int x = 10;
    return x;
}

// 错误2:返回临时对象引用
const std::string& badRef2() 
{
    return "temporary";
}

// 错误3:返回悬空指针的引用
const std::string* badPtr() 
{
    std::string s = "hello";
    return &s;
}

4、const引用返回:

  • 当不想让调用者修改返回值时
  • 但仍想避免拷贝开销时
代码语言:javascript
复制
const std::string& getConfigValue() const 
{
    static std::string value = loadConfig();
    return value;
}

5、返回值优化(RVO/NRVO)

  • 现代编译器会优化传值返回的拷贝操作
代码语言:javascript
复制
// 以下代码通常不会产生实际拷贝
std::vector<int> makeVector() 
{
    return std::vector<int>{1, 2, 3};
}

四、结合示例比较性能

代码演示如下——

代码语言:javascript
复制
#include <chrono>
#include <vector>

// 传值返回版本
std::vector<int> createVectorByValue(int size) {
    return std::vector<int>(size, 42);
}

// 传引用返回版本(通过输出参数)
void createVectorByRef(int size, std::vector<int>& out) {
    out.assign(size, 42);
}

void testPerformance() {
    const int size = 10000000;
    
    // 测试传值返回
    auto start1 = std::chrono::high_resolution_clock::now();
    auto v1 = createVectorByValue(size);
    auto end1 = std::chrono::high_resolution_clock::now();
    
    // 测试传引用返回
    std::vector<int> v2;
    auto start2 = std::chrono::high_resolution_clock::now();
    createVectorByRef(size, v2);
    auto end2 = std::chrono::high_resolution_clock::now();
    
    auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(end1 - start1);
    auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(end2 - start2);
    
    std::cout << "By value: " << duration1.count() << " μs\n";
    std::cout << "By ref: " << duration2.count() << " μs\n";
}

注意:由于RVO优化,传值返回的性能可能与传引用返回相当甚至更好。


结尾

往期回顾:

C++引用专题(上):详解C++传值返回和传引用返回

【C/C++】C++引用和指针的对比

【C/C++】形参、实参相关内容整理

【C/C++】Dev-C++的安装与使用以及快捷键整理

【日常问题解决方案】VS2022不小心解决方案资源管理器把关掉了怎么办

VS2022进行监视功能的步骤

结语:本文内容到这里就全部结束了。本文我们对比了C++传值返回和传引用返回的概念,并且结合具体示例,比较了性能,希望对大家有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正文:对比传值返回和传引用返回
    • 一、基本概念对比
    • 二、结合示例
      • 传值返回示例
      • 传引用返回示例
      • 右值引用返回 (C++11)
    • 三、最佳实践指南
    • 四、结合示例比较性能
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档