C++引用专题(上):详解C++传值返回和传引用返回
🔥个人主页:艾莉丝努力练剑
❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、C/C++干货分享&学习过程记录
🍉学习方向:C/C++方向
⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平
前言:本专栏记录了博主C++从初阶到高阶完整的学习历程,会发布一些博主学习的感悟、碰到的问题、重要的知识点,和大家一起探索C++这门程序语言的奥秘。这个专栏将记录博主C++语法、高阶数据结构、STL的学习过程,正所谓“万丈高楼平地起”嘛,我们话不多说,继续进行C++阶段的学习。本文我们不讲C++主线的内容,我们来拓展一下或者说整理一下我们学习时C/C++时经常会提到的一些专有名词,例如形参、实参,显式类型转换和隐式类型转换类型转换,内置类型、引用、传值返回和传引用返回等等。
C++的两个参考文档:
老朋友(非官方文档):cplusplus
官方文档(同步更新):cppreference
目录
正文
一、传值返回详解
1、基本概念
2、典型代码示例
3、底层工作原理
4、编译器优化(RVO/NRVO)
5、适用场景
6、优点
7、缺点
二、传引用返回详解
1、基本概念
2、典型代码示例
3、底层工作原理
4、生命周期注意事项
5、适用场景
6、优点
7、缺点
三、对比总结表
四、C++实践
1、默认优先使用传值返回:
2、明确使用引用返回的场景:
3、结合const正确性:
4、C++11后的改进:
5、性能关键代码的考量:
结尾
正文
博主之前写了传值返回和传引用返回相关的内容,只不过那是在C++初识部分介绍的,比较简略:
【C/C++】初识C++(二):深入详解缺省参数(默认参数)函数重载、引用(重头戏)
【C/C++】初识C++(三):C++入门内容收尾——const引用,指针和引用关系梳理,inline(内联函数),nullptr替代NULL
一、传值返回详解
1、基本概念
传值返回是指函数返回对象的一个副本,调用方获得的是与原对象完全独立的新对象。
2、典型代码示例
// 基本类型传值返回
int add(int a, int b)
{return a + b; // 返回int类型的值
}// 对象传值返回
std::string createGreeting(const std::string& name)
{return "Hello, " + name + "!"; // 返回新构造的string对象
}// 返回局部对象(安全)
std::vector<int> generateNumbers()
{std::vector<int> nums {1, 2, 3, 4, 5};return nums; // 返回拷贝(可能被RVO优化)
}
3、底层工作原理
-
函数内部创建要返回的对象
-
将该对象拷贝到调用方的接收位置(可能被编译器优化)
-
函数栈帧销毁,局部变量被清理
4、编译器优化(RVO/NRVO)
-
RVO (Return Value Optimization): 编译器消除临时对象的构造和拷贝
std::string func()
{return std::string("hello"); // 可能直接在调用方内存构造
}
-
NRVO (Named Return Value Optimization): 对命名局部变量的优化
std::string func()
{std::string s = "hello";return s; // 可能直接在调用方内存构造
}
5、适用场景
-
返回基本类型(int、double等)
-
返回小型结构体或简单对象
-
需要返回独立副本的情况
-
返回局部变量时
6、优点
-
安全性高,不会产生悬空引用
-
接口清晰,调用方获得独立对象
-
现代C++编译器优化后性能良好
7、缺点
-
对于大型对象可能有拷贝开销(未优化时)
-
无法通过返回值修改原对象
二、传引用返回详解
1、基本概念
传引用返回是指函数返回对象的引用(别名),调用方获得的是对原对象的直接引用。
2、典型代码示例
// 返回静态变量的引用(安全)
const std::string& getVersion() {static const std::string ver = "1.0.0";return ver;
}// 返回成员变量的引用
class Container {std::vector<int> data;
public:// const引用返回const std::vector<int>& getData() const { return data; }// 非const引用返回(允许修改)std::vector<int>& getMutableData() { return data; }
};// 返回参数引用(需确保参数生命周期)
const std::string& longerString(const std::string& a, const std::string& b) {return a.length() > b.length() ? a : b;
}
3、底层工作原理
-
函数计算要返回的引用(地址)
-
将该地址返回给调用方
-
调用方通过引用直接访问原对象
4、生命周期注意事项
// 危险示例1:返回局部变量引用
const int& dangerous1() {int x = 42;return x; // x将被销毁!
}// 危险示例2:返回临时对象引用
const std::string& dangerous2() {return "temporary"; // 临时对象将销毁!
}// 危险示例3:返回悬空指针的引用
const std::string& dangerous3() {std::string* s = new std::string("hello");return *s; // 内存泄漏风险
}
5、适用场景
-
返回类成员变量
-
返回静态/全局变量
-
返回参数中生命周期有保障的对象
-
需要允许调用者修改原对象时
-
返回大型对象避免拷贝时
6、优点
-
无拷贝开销,性能高
-
允许修改原对象(非const引用时)
-
可以用于实现链式调用
7、缺点
-
必须确保被引用对象的生命周期
-
不当使用会导致未定义行为
-
接口契约更复杂(需要文档说明生命周期
三、对比总结表
比较维度 | 传值返回 | 传引用返回 |
---|---|---|
返回内容 | 对象副本 | 对象别名 |
拷贝开销 | 可能有(但常被优化) | 无 |
修改原对象 | 不能 | 可以(非const引用) |
安全性 | 高 | 需注意生命周期 |
适用对象大小 | 小型对象 | 中大型对象 |
典型用例 | 工厂函数、计算函数 | 访问器、链式调用 |
RVO优化 | 支持 | 不适用 |
C++11移动语义 | 可结合使用 | 不适用 |
四、C++实践
1、默认优先使用传值返回:
// 现代C++编译器能很好优化
std::vector<int> getData()
{return {1, 2, 3}; // 依赖RVO/NRVO
}
2、明确使用引用返回的场景:
// 场景1:返回类成员
class Config
{std::string path;
public:const std::string& getPath() const { return path; }
};// 场景2:链式调用
class Builder
{std::string data;
public:Builder& append(const std::string& s) {data += s;return *this;}
};
3、结合const正确性:
// 只读访问用const引用
const std::string& getName() const;// 需要修改用非const引用
std::string& getMutableName();
4、C++11后的改进:
// 移动语义支持
std::unique_ptr<Resource> createResource()
{auto res = std::make_unique<Resource>();res->initialize();return res; // 移动而非拷贝
}// 返回右值引用
std::vector<int>&& extractData()
{std::vector<int> temp;// ...填充数据return std::move(temp); // 明确所有权转移
}
5、性能关键代码的考量:
// 参数+返回优化
void computeResult(Input in, Result& out)
{// 直接填充out避免拷贝
}// 或者返回智能指针
std::shared_ptr<BigData> createBigData()
{return std::make_shared<BigData>();
}
我们在选择具体的返回方式需要综合考虑对象生命周期、性能需求、接口设计等多个因素。
结尾
往期回顾:
【C/C++】C++引用和指针的对比
【C/C++】形参、实参相关内容整理
【C/C++】Dev-C++的安装与使用以及快捷键整理
【日常问题解决方案】VS2022不小心解决方案资源管理器把关掉了怎么办
VS2022进行监视功能的步骤
结语:本文内容到这里就全部结束了。本文我们重新整理了C++传值返回和传引用返回相关的内容,希望对大家有所帮助。