
🔥个人主页:艾莉丝努力练剑 ❄专栏传送门:《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
特性 | 传值返回 | 传引用返回 |
|---|---|---|
返回内容 | 返回对象的副本 | 返回对象的引用 |
内存开销 | 可能有拷贝开销 | 无额外拷贝开销 |
修改原对象 | 不能修改原对象 | 可以直接修改原对象 |
适用场景 | 返回局部变量或需要独立副本的情况 | 返回成员变量或静态变量等长期存在的对象 |
安全性 | 高(不会产生悬空引用) | 需注意生命周期(可能产生悬空引用) |
// 返回局部变量的拷贝(安全)
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());
}// 返回静态变量的引用(安全)
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();
}// 工厂函数返回右值
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、优先传值返回:
2、谨慎使用传引用返回:
3、避免常见错误:
// 错误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引用返回:
const std::string& getConfigValue() const
{
static std::string value = loadConfig();
return value;
}5、返回值优化(RVO/NRVO):
// 以下代码通常不会产生实际拷贝
std::vector<int> makeVector()
{
return std::vector<int>{1, 2, 3};
}代码演示如下——
#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优化,传值返回的性能可能与传引用返回相当甚至更好。
往期回顾:
【日常问题解决方案】VS2022不小心解决方案资源管理器把关掉了怎么办
结语:本文内容到这里就全部结束了。本文我们对比了C++传值返回和传引用返回的概念,并且结合具体示例,比较了性能,希望对大家有所帮助。