值传递 vs 引用传递 实践思考笔记
核心代码基础(对应题干代码):
1 |
|
一、实践思考题标准答案
1. 删掉11~16行拷贝构造,编译器会自动生成吗?是平凡拷贝吗?
✅ 会自动生成默认拷贝构造函数;属于逐成员浅拷贝(非平凡拷贝,仅当类为“平凡类型”时才是平凡拷贝)。
补充:默认拷贝构造的行为的是对所有成员变量逐一向拷贝——int类型直接复制值,std::string类型调用其自身的拷贝构造,指针类型仅复制地址(不复制指向内容)。
2. 删掉类的m_str变量,观察函数的调用差异?
删掉m_str后,Test类仅包含int类型成员,成为平凡类型(trivial type),编译器会优化调用逻辑:
调用test1(值传递)时,不调用拷贝构造函数,直接通过memcpy复制对象内存(8字节,两个int),效率极高;
test2(引用传递)无变化,依旧仅传递对象地址,零开销;
汇编中无“call 拷贝构造”指令,test1内部仅保留析构调用(清理栈上副本)。
3. 对比test1(24行)和test2(28行)不同函数参数调用的区别?
核心区别:值传递产生副本(有开销),引用传递仅传地址(零开销),具体对比如下:
二、值传递(test1)vs 引用传递(test2)核心对比
| 对比维度 | test1(Test ts)值传递 | test2(Test& ts)引用传递 |
|---|---|---|
| 参数本质 | 在栈上新建一个对象副本 | 传递原对象的内存地址(本质是指针) |
| 构造/析构调用 | 调用1次拷贝构造(生成副本)+ 1次析构(销毁副本) | 无任何构造、析构调用 |
| 有m_str(string)时 | 开销大幅增加(需拷贝string内容、分配内存) | 依旧零开销(仅传地址) |
| 汇编指令 | 指令多,包含call拷贝构造、call析构 | 指令极少,内部为空操作(push/pop ebp后直接ret) |
| 适用场景 | 仅用于int等简单小类型(开销可忽略) | 复杂类型(如带string、指针的类)永远优先使用 |
三、关键补充(结合汇编重点)
值传递的核心开销:副本的拷贝与销毁,尤其是包含std::string、动态内存等资源时,开销会急剧增加;
引用传递的核心优势:不产生副本,直接操作原对象(或仅传地址),无任何额外开销;
平凡类型(仅含int、char等基础类型):编译器会优化拷贝行为,无需调用拷贝构造,直接内存复制,效率接近值传递的简单类型。
四、总结(必记重点)
未手动编写拷贝构造时,编译器自动生成默认拷贝构造,执行浅拷贝;
类无复杂成员(如string、指针)时,成为平凡类型,拷贝行为被编译器优化;
复杂类型的函数参数,优先使用引用传递,避免拷贝构造和析构的额外开销。
关于初始化的实践思考笔记
一、先看代码(你要分析的 5~7 行)
1 | struct TestResult { |
核心结论(直接写答案)
第5~7行的作用:给成员变量做【类内初始化】,保证变量一定有初始值,避免未定义行为。
二、逐行解释作用(最标准)
1. double correctionTime {};
✅ 作用:值初始化(零初始化)
- 空大括号
{}→ 强制初始化为 0.0 - 不管在哪里定义对象,这个变量永远不会是随机值
2. bool parseSuccess = true;
✅ 作用:类内默认初始化
- 直接赋值
= true - 对象创建时,默认就是
true
3. int correctionSuccessNum {};
✅ 作用:值初始化(零初始化)
- 空大括号
{}→ 强制初始化为 0 - 绝对不会出现随机垃圾值
三、这三行真正的目的
1. 防止成员变量出现随机垃圾值
如果不写初始化:
1 | int correctionSuccessNum; // 不初始化 |
那么它的值是随机的、不确定的,会导致:
- 程序逻辑错误
- 崩溃
- 未定义行为
2. 统一初始化风格,代码更安全
C++11 推荐写法:
{}→ 零初始化(最安全)= 值→ 默认初始化
3. 不管对象怎么创建,都有确定值
无论你怎么定义:
1 | TestResult result; |
成员变量一定有初始值,不会出现野值。
四、最关键的区别
| 写法 | 效果 |
|---|---|
int num; |
随机值(危险) |
int num{}; |
0(安全) |
bool b = true; |
true(安全) |
double d{}; |
0.0(安全) |
五、最终总结
第 5~7 行代码的作用:
- 对结构体成员变量进行类内初始化,保证变量创建时就拥有确定的初始值;
- 避免未初始化带来的随机垃圾值、未定义行为、程序崩溃;
- 使用
{}进行值初始化(零初始化),使用= true进行默认初始化; - 让结构体无论在任何场景下创建对象,成员变量都保持安全、确定的状态。