网站虚拟主机行吗,wordpress容易被黑么,标志设计图片,搜索引擎推广的特点目录
1、输入参数的传递方式-选择传值还是传引用#xff1f;
处理用户信息
处理坐标
处理配置
处理ID
2、对于需要修改的参数,使用非const引用传递 有趣的例外#xff1a;警惕表象的迷惑
需要警惕的陷阱
“糟糕”的update方法#xff1a;
“完美”的set_name与set…目录
1、输入参数的传递方式-选择传值还是传引用
处理用户信息
处理坐标
处理配置
处理ID
2、对于需要修改的参数,使用非const引用传递 有趣的例外警惕表象的迷惑
需要警惕的陷阱
“糟糕”的update方法
“完美”的set_name与set_age方法
总结与最佳实践
如何避免掉进这些坑?
利用编译器的警告和错误
遵循const正确性
明确函数意图
使用std::optional或返回值
代码审查和测试
了解编译器的特性
持续学习和实践
3、为什么要采用移动语义
如何实现移动语义
移动语义的实战应用
注意事项
总结
例
代码分析
潜在陷阱
结论
4、返回值 vs 输出参数
返回值
输出参数
栗子
什么时候该用输出参数
返回多个值
5、指针 VS 引用
引用的特点
指针的特点
选择指针还是引用 你是否曾凝视着代码心中浮现出这些疑惑
这个参数更适合按值传递还是按引用传递呢为何我的程序运行如此缓慢是否与参数的传递方式有关移动语义背后究竟隐藏着怎样的奥秘在需要返回多个值时是选择使用元组tuple还是结构体struct更为合适
若有那就继续Look Look...
1、输入参数的传递方式-选择传值还是传引用 这是一个在C编程中经常让人纠结的问题。让我们来探讨一下这个问题的黄金法则 当参数是“轻量级”的数据类型例如int、指针等小型变量时选择传值是一个明智之举。这是因为传值操作直接且高效无需担心指针解引用的额外开销。 然而当参数是“重量级”的数据类型例如string这种大型对象时传const引用则显得更为合适。这是因为传const引用可以避免不必要的对象拷贝从而节省时间和内存资源。 那么为什么会这样呢原因如下 传值的好处在于其简单直接性无需处理复杂的指针操作减少了出错的可能性。 而传const引用的好处则在于其效率性特别是对于大型对象而言避免了拷贝的开销使得程序运行更加高效。 在处理不同类型的数据时选择正确的参数传递方式至关重要。下面是对您提供的代码片段中参数传递方式的点评及优化建议
处理用户信息
推荐方式void processUserInfo(const string name); 理由字符串string在C中通常是一个相对较大的数据结构包含动态分配的内存。因此通过const引用传递可以避免不必要的拷贝提高程序效率。不推荐方式void processUserInfo(string name); 理由这种方式会导致每次调用函数时都进行字符串的拷贝增加了不必要的内存分配和释放操作降低了程序性能。
处理坐标
推荐方式void movePoint(int x, int y); 理由整数int是基本数据类型占用内存小传值操作高效且直接。不推荐方式void movePoint(const int x, const int y); 理由对于基本数据类型使用引用传递反而会增加额外的开销如指针解引用而且整数本身就很小拷贝的开销几乎可以忽略不计。
处理配置
推荐方式void updateConfig(const vectorint config); 理由vectorint可能包含大量的整数元素占用较大的内存空间。通过const引用传递可以避免整个向量的拷贝提高程序效率。不推荐方式void updateConfig(vectorint config); 理由这种方式会导致每次调用函数时都进行向量的拷贝包括动态内存分配和元素复制开销很大。
处理ID
推荐方式void processId(int id); 理由ID通常是整数类型占用内存小传值操作高效且直接。不推荐方式void processId(const int id); 理由同样地对于基本数据类型如整数ID使用引用传递是不必要的因为整数本身就很小拷贝的开销几乎可以忽略不计。 综上所述在选择参数传递方式时应根据数据类型的大小和特性来决定。对于大型数据结构如字符串、向量等使用const引用传递可以避免不必要的拷贝而对于小型数据结构如基本数据类型则可以直接传值。 2、对于需要修改的参数,使用非const引用传递 当需要修改函数内部的参数时我们应选择使用非const引用来传递它。这样做的原因何在呢让我们通过一个生动的比喻来揭示其重要性。 想象一下你有一把珍贵的钥匙需要交给朋友去开门。但你不确定他是否会私自复制这把钥匙。这种不确定性就像你把一个参数传给函数时却不清楚它是否会在函数内部被修改。 为了避免这种潜在的风险使用非const引用就像是给参数加上一个醒目的标签“注意这个参数我可能会在函数内部进行修改哦”这样一来代码的意图就变得清晰明了任何阅读你代码的人都能立刻明白这个参数的用途和可能的变化。 非const引用的这种明确性不仅提高了代码的可读性还增强了代码的安全性。它确保了函数内部对参数的修改是可控和预期的从而避免了因参数被意外修改而导致的潜在错误。 因此在需要修改函数内部参数的情况下选择非const引用来传递参数是一个明智且安全的选择。 举例 // result命名不明确无法直观判断其用途
void calculate_sum(int values[], int count, int* result); // sum作为输出参数明确表示其将被函数修改以存储结果
void calculate_sum(const int values[], int count, int sum);// 使用char*类型的str作为参数其修改状态不明确
void parse_name(char* str);// 使用string引用作为参数直观表达其将被修改
void parse_name(std::string str);有趣的例外警惕表象的迷惑 有些数据类型看似温和无害实则暗藏玄机能在不经意间修改原始对象。换句话说就是有些类型表面上看起来安全无害但实际上它们具有修改原始对象的能力这种能力往往不易被察觉。
#include vector
#include memoryclass Widget {std::vectorint data;
public:void add(int x) { data.push_back(x); }
};// process函数接收一个shared_ptrWidget的按值传递参数
// 但由于shared_ptr管理的是一个动态分配的对象
// 因此通过解引用shared_ptr即w-add(42)可以修改该对象的状态
void process(std::shared_ptrWidget w)
{w-add(42); // 这里的操作看似按值传递但实则修改了w所指向的Widget对象
}// update_iterator函数接收一个vectorint::iterator的按值传递参数
// 迭代器本质上是一个指向vector中元素的指针封装
// 因此通过解引用迭代器即*it 100可以修改vector中对应元素的值
void update_iterator(std::vectorint::iterator it)
{*it 100; // 这里的操作虽然也是按值传递迭代器但实则修改了迭代器所指向的vector元素
}
详细解释
process函数 参数w是一个std::shared_ptrWidget的按值传递实例。尽管w是按值传递的但它所管理的Widget对象是动态分配的并且多个shared_ptr实例可能共享对该对象的所有权。通过w-add(42)我们实际上是在修改w所指向的Widget对象的状态向其data成员添加了一个新元素。因此尽管w本身是按值传递的但其所指向的对象的状态是可以被修改的。update_iterator函数 参数it是一个std::vectorint::iterator的按值传递实例。迭代器it实际上是一个封装了指向vectorint中某个元素的指针的对象。通过*it 100我们解引用了迭代器并修改了它所指向的vectorint中的元素的值。因此尽管it本身是按值传递的但其所指向的vectorint元素的值是可以被修改的。
总结 在这两个场景中尽管参数是按值传递的但由于它们分别指向或封装了可以修改的对象或元素因此仍然可以产生副作用。这提醒我们在编写代码时要格外小心确保理解参数的实际含义和它们所指向或封装的内容。
需要警惕的陷阱 引用参数这一编程中的利器犹如一把双刃剑既能够作为输入传递数据又能够作为输出返回结果。然而若使用不当这把剑也可能反过来伤到自己带来意想不到的问题。 在编程实践中引用参数因其能够直接修改原始数据而备受青睐。这种特性使得函数能够高效地处理大型数据结构而无需进行繁琐的数据复制。然而正是这种直接修改的能力也带来了潜在的风险。 当函数通过引用参数接收数据时调用者可能并不清楚数据会在函数内部被如何修改。如果函数内部对数据进行了意外的修改那么调用者的数据状态也可能随之发生变化从而导致程序行为异常。 此外即使函数本身没有意图修改数据但由于引用参数的存在其他函数或代码段也可能通过该引用间接地修改数据。这种隐蔽的数据修改方式往往难以追踪和调试从而增加了程序的复杂性和维护成本。 因此在使用引用参数时我们需要格外小心。要明确函数对引用参数的修改意图并在文档中清晰地说明这一点。同时也要谨慎地选择是否使用引用参数以避免不必要的复杂性和潜在的风险。 总之引用参数是一把双刃剑既能带来便利也可能带来麻烦。只有在使用时保持警惕和谨慎才能确保程序的正确性和稳定性。
“糟糕”的update方法
void update(Person p) { *this p; // 危险操作完全替换了当前对象
} 此方法名为update但实际操作却如同“夺舍”——它完全用传入的Person对象p替换了当前对象的状态。这种操作极具破坏性因为它不仅更改了对象的所有属性还忽略了调用者可能只想更新部分属性的需求。此外若p是一个临时对象或即将被销毁的对象这种替换可能导致资源泄露或悬挂指针等潜在问题。
“完美”的set_name与set_age方法
void set_name(const string new_name) { name_ new_name; }void set_age(int new_age) { if (new_age 0) throw invalid_argument(年龄不能为负,你想穿越吗?);age_ new_age;
} 相比之下set_name和set_age方法则如同两位专业的美容师它们温柔地、有选择地更新对象的属性。set_name方法仅修改name_属性而set_age方法不仅修改age_属性还在赋值前进行有效性检查确保年龄不会设为负数。这种方法既安全又灵活因为它允许调用者精确地控制哪些属性需要更新。
总结与最佳实践
避免使用“夺舍”式的update方法它们破坏了对象的封装性可能导致不可预测的状态变化。采用“美容师”式的属性设置方法通过提供明确的、有选择性的属性设置方法如set_name和set_age可以更好地控制对象的状态变化并确保属性的有效性。遵循最小权限原则仅暴露必要的接口给外部使用以减少潜在的安全风险和维护成本。 通过遵循这些最佳实践可以创建更加健壮、可维护和安全的代码。
如何避免掉进这些坑?
要避免掉进编程中的陷阱特别是与引用参数相关的陷阱我们可以依靠编译器的帮助并遵循一些最佳实践。以下是一些建议帮助你编写更加健壮和清晰的代码 利用编译器的警告和错误 现代编译器非常智能它们能够识别出许多潜在的代码问题。例如如果你声明了一个非const引用参数但在函数体内没有修改它一些编译器可能会给出警告提示你可能忘记了修改或者这个参数应该被声明为const。同样如果你不小心对一个非const引用参数执行了移动操作std::move编译器也可能会警告你这样做可能会导致数据丢失或未定义行为。 遵循const正确性 默认情况下应将函数参数声明为const引用除非你确实需要在函数内部修改它们。这有助于保护数据不被意外修改并增加代码的可读性和可维护性。如果函数不需要修改参数使用const引用可以避免不必要的复制同时保证数据的安全性。 明确函数意图 通过函数命名和文档清晰地表达函数的意图。如果函数旨在修改参数那么使用非const引用是合适的。但是如果函数只是读取参数则应使用const引用。 使用std::optional或返回值 如果函数有时需要修改参数有时又不需要可以考虑使用std::optionalT在C17及更高版本中可用来明确指示哪些参数可能被修改。另一种方法是让函数返回一个新的对象或值而不是修改输入参数。这有助于保持函数的纯净性即无副作用。 代码审查和测试 定期进行代码审查让团队成员相互检查代码以发现潜在的错误和陷阱。编写单元测试来验证函数的行为确保它们按预期工作。 了解编译器的特性 不同的编译器可能有不同的警告和错误消息。了解你所使用的编译器的特性并启用尽可能多的警告选项可以帮助你发现更多的问题。 持续学习和实践 编程是一个不断学习和实践的过程。通过阅读文档、参加培训课程、参与社区讨论等方式不断提高自己的编程技能。 记住编译器是我们的好朋友但它并不能捕捉到所有的错误。因此我们还需要依靠良好的编程习惯、代码审查和测试来确保代码的质量和可靠性。遵循这些建议你将能够避免许多常见的陷阱并编写出更加健壮和清晰的代码。
在C的舞台上移动对象这出戏确实是一场精彩绝伦的表演。它关乎于如何高效地传递大型对象避免不必要的复制从而提升程序的性能。接下来我们就来深入探讨一下这场表演的艺术所在。
3、为什么要采用移动语义 在C中当我们需要传递或返回大型对象时如果采用值传递的方式编译器会生成该对象的副本。对于大型对象而言这个过程可能会消耗大量的时间和内存。然而很多时候我们并不需要在源位置保留这个对象此时移动语义就派上了用场。 移动语义允许我们“偷走”对象的资源如动态分配的内存、文件句柄等而不是复制它们。这样目标对象就可以接管这些资源而源对象则变为一个有效但未定义状态通常称为“空”或“已移动”状态。这种方式可以极大地提高性能特别是在处理大型数据结构时。
如何实现移动语义 在C中实现移动语义通常需要以下两个步骤 定义移动构造函数这是一个特殊的构造函数它接受一个右值引用T作为参数。右值引用是C11引入的一种新特性它允许我们区分左值如变量、函数返回值等和右值如临时对象、std::move的返回值等。移动构造函数会“偷走”传入对象的资源而不是复制它们。 定义移动赋值运算符这与移动构造函数类似但它用于赋值操作。它同样接受一个右值引用作为参数并“偷走”传入对象的资源。
移动语义的实战应用 在标准库中许多容器和字符串类都实现了移动语义。例如std::vector和std::string都提供了移动构造函数和移动赋值运算符。这使得我们可以高效地传递和返回这些类型的对象。 此外std::move函数也是一个非常重要的工具。它可以将一个左值强制转换为右值引用从而允许我们调用移动构造函数或移动赋值运算符。但请注意std::move本身并不移动任何东西它只是改变了对象的值类别使得移动操作成为可能。
注意事项 虽然移动语义可以极大地提高性能但我们也必须小心使用。一旦对象被移动它的状态就不再有效。因此在移动对象后我们应该避免再使用它除非我们明确知道它的新状态。此外移动语义还可能与对象的析构函数和资源管理策略相互作用因此我们需要确保这些方面都得到妥善处理。
总结 在C的舞台上移动对象这出戏确实是一场值得一看的表演。通过实现移动构造函数和移动赋值运算符并合理使用std::move函数我们可以高效地传递和返回大型对象从而提升程序的性能。但请记住移动语义是一把双刃剑在使用它时我们需要小心谨慎地处理对象的状态和资源管理问题。
例 在C中您所展示的代码片段巧妙地运用了移动语义来优化性能。然而这里有一些细微之处和潜在陷阱需要注意。首先让我们来分析一下代码
string make_greeting(string name) {string result Hello, ;result std::move(name); // 直接“偷”走name的内容return result; // result也会被移动返回效率极高
}// 使用示例
string name Alice;
string greeting make_greeting(std::move(name));
// 此时name变为空字符串greeting则包含了完整的问候语
代码分析 函数签名string make_greeting(string name) 表明该函数接受一个右值引用到string类型的参数。这允许函数“偷”走传入string对象的资源因为右值引用通常用于表示可以安全移动的对象。 移动操作在函数体内std::move(name)被用于将name的内容“转移”给result。这里std::move并不真正移动数据而是将name转换为右值引用从而允许编译器选择移动赋值运算符如果可用来优化性能。 返回值函数返回result这是一个局部变量。在C11及更高版本中返回局部变量时如果类型支持移动语义编译器通常会使用命名返回值优化NRVO或移动语义来避免不必要的复制。然而即使没有这个优化std::string的拷贝构造函数和赋值运算符通常也被高度优化因此这里的性能差异可能并不显著除非string非常大或复制操作非常昂贵。 调用后的状态由于name被std::move转换并传递给make_greeting它在函数返回后处于未定义状态但在这个特定情况下由于std::string的移动构造函数会清空源字符串所以name变为空字符串。这意味着在调用make_greeting后不应再使用name。
潜在陷阱 重复移动虽然在这个例子中std::move的使用是合理的但在某些情况下过度使用std::move可能会导致不必要的移动操作从而损害性能。例如如果name在传递给make_greeting之前或之后还需要被使用那么就不应该对它使用std::move。 未定义状态记住在移动操作后源对象在这个例子中是name处于未定义状态。因此在移动之后使用它是不安全的。 命名返回值优化NRVO虽然NRVO可能会减少或消除返回result时的复制或移动操作但这不是程序员可以依赖的行为。它取决于编译器的实现和优化级别。
结论
您的代码示例展示了如何在C中使用移动语义来优化性能。然而在实际编程中需要谨慎使用std::move和移动语义以避免潜在的陷阱和未定义行为。同时也要意识到现代C编译器在优化返回值方面已经做得非常出色因此有时不必过度担心性能问题。
4、返回值 vs 输出参数
返回值
优点 直观性这种方法非常直观因为函数直接返回所需的结果。调用者无需准备额外的变量来接收结果。 简洁性代码更加简洁因为不需要额外的参数来传递输出。 链式调用返回值允许链式调用即一个函数的返回值可以作为另一个函数的参数。
缺点 大型对象对于大型对象或复杂数据结构返回值的开销可能较大因为可能需要复制或移动数据。 错误处理如果函数可能失败并需要报告错误返回值可能变得复杂特别是当成功和失败的情况都需要返回不同类型的数据时。
输出参数
优点 避免复制对于大型对象使用输出参数可以避免复制或移动数据因为数据是直接在调用者提供的变量中修改的。 灵活性输出参数允许函数返回多个结果或者通过引用修改输入参数。 错误处理在某些情况下输出参数可以更容易地处理错误例如通过引用传递一个错误代码或状态。
缺点 不直观对于不熟悉的人来说输出参数可能不太直观因为它们需要额外的变量来接收结果。 易出错如果调用者忘记提供有效的输出参数例如传递了一个空指针或未初始化的引用则可能导致未定义行为或崩溃。 链式调用受限输出参数通常不支持链式调用因为函数不返回任何有用的结果或只返回一个状态码。 在实际编程中选择返回值还是输出参数取决于具体的情况和需求。对于小型、简单的数据结构返回值通常是更好的选择因为它更直观且易于使用。对于大型、复杂的数据结构或需要返回多个结果的情况输出参数可能更合适因为它可以避免不必要的复制和移动操作并提供更大的灵活性。
栗子
#include iostreamint add(int a, int b) {return a b;
}int main() {int x 5;int y 10;int sum add(x, y);std::cout Sum (return value): sum std::endl;return 0;
}
#include iostreamvoid add(int a, int b, int result) {result a b;
}int main() {int x 5;int y 10;int sum;add(x, y, sum);std::cout Sum (output parameter): sum std::endl;return 0;
}
什么时候该用输出参数 在C编程中选择是否使用输出参数通常通过引用或指针传递取决于多个因素包括函数的目的、需要返回的数据量、以及是否希望修改传入的参数等。以下是一些指导原则帮助你在自定义函数中决定何时使用输出参数 需要修改传入的参数 如果函数需要修改其传入的参数并且这些修改对调用者是有意义的那么应该使用输出参数。例如一个排序函数可能需要传入一个数组并对其进行原地排序。 返回多个值 当函数需要返回多个值时使用输出参数是一种常见的方法。C函数只能有一个返回值但如果有多个结果需要返回给调用者那么可以通过输出参数来实现。 避免不必要的复制 对于大型对象或数据结构如果通过返回值传递会导致不必要的复制那么使用输出参数可能更高效。通过引用或指针传递可以避免复制从而节省时间和内存。 保持接口的一致性 如果函数的接口需要与现有的API或库保持一致并且这些接口已经使用了输出参数那么为了保持一致性你的函数也应该使用输出参数。 函数的目的和语义 考虑函数的目的和它所表达的语义。有时候使用输出参数可以使函数的意图更加清晰。例如一个查找函数可能通过输出参数返回找到的元素的索引并通过返回值表示是否找到了该元素。 错误处理和异常安全 使用输出参数有时可以更容易地处理错误和异常。例如如果函数在执行过程中遇到错误它可以通过输出参数返回部分结果或状态信息同时设置错误代码或抛出异常。 调用者的便利性 考虑调用者的便利性。有时候使用输出参数可以使调用者的代码更加简洁和直观。例如如果调用者已经有一个变量来存储结果并且希望直接更新这个变量那么使用输出参数可能更方便。 然而也需要注意过度使用输出参数可能会导致函数接口变得复杂和难以理解。因此在决定使用输出参数时应该权衡其优缺点并确保函数接口的设计是清晰、直观和易于使用的。
返回多个值 在C中当你需要从一个函数返回多个值时有几种推荐的方式可以考虑。以下是一些常见的方法 使用结构体或类 定义一个结构体或类来封装需要返回的多个值。这种方法具有良好的可读性和类型安全性。 struct Result {int value1;double value2;std::string message;
};Result getValues() {Result result;result.value1 42;result.value2 3.14;result.message Success;return result;
} 使用std::tuplestd::tuple是C11引入的一个模板类用于存储固定大小的异构值集合。它提供了一种轻量级的方式来返回多个值而不需要定义新的结构体或类。 #include tuple
#include stringstd::tupleint, double, std::string getValues() {return std::make_tuple(42, 3.14, Success);
}// 使用时可以通过std::get来获取值
int main() {auto [value1, value2, message] getValues(); // C17结构化绑定// 或者在C11/14中使用std::get// int value1 std::get0(getValues());// double value2 std::get1(getValues());// std::string message std::get2(getValues());return 0;
} 注意在C17中你可以使用结构化绑定来简化从std::tuple中提取值的语法。 使用输出参数 通过引用或指针将值作为参数传递给函数并在函数内部进行赋值。这种方法适用于需要修改传入参数或避免复制大型对象的情况。 void getValues(int value1, double value2, std::string message) {value1 42;value2 3.14;message Success;
}int main() {int value1;double value2;std::string message;getValues(value1, value2, message);return 0;
} 然而这种方法可能会使函数签名变得复杂并且需要调用者准备适当的变量来接收输出。 使用std::pair 如果只需要返回两个值并且这两个值的类型不同那么可以使用std::pair。 #include utility // 包含std::pairstd::pairint, double getValues() {return std::make_pair(42, 3.14);
}int main() {auto [value1, value2] getValues(); // C17结构化绑定// 或者在C11/14中使用std::get但这里不适用因为std::pair没有std::tuple的std::get函数应该直接访问first和second// int value1 getValues().first;// double value2 getValues().second;return 0;
} 注意尽管std::pair只能存储两个值并且这两个值的类型在编译时是固定的但它比std::tuple更轻量并且在只需要返回两个值的情况下更为方便。 总的来说选择哪种方法取决于你的具体需求、代码的可读性和维护性考虑。对于返回多个异构值的情况std::tuple和结构体/类是很好的选择。如果你只需要返回两个值并且这两个值的类型不同那么std::pair可能是一个更简单的选择。输出参数适用于需要修改传入参数或避免复制大型对象的情况但可能会使函数签名变得复杂。
5、指针 VS 引用
在C编程中指针和引用都是用于间接访问变量的工具但它们有不同的使用场景和特性。选择使用指针还是引用通常取决于你的具体需求、代码的可读性、安全性以及潜在的性能考虑。
引用的特点 安全性引用在创建时必须被初始化且之后不能再改变指向。这有助于避免悬挂指针dangling pointer和野指针wild pointer等问题。 语法简洁使用引用可以使代码更加简洁和直观因为它们提供了类似变量的语法。 无法为空引用不能为空null这意味着你不能创建一个未初始化的引用。这有时是一个优点因为它可以强制你在使用引用之前对其进行初始化。 不支持算术运算引用不支持指针算术运算这有助于防止一些常见的指针错误。
指针的特点 灵活性指针可以指向任何有效的内存地址包括空指针null。这使得指针在需要表示“无值”或“未初始化”状态时非常有用。 动态内存管理指针常用于动态内存分配和释放这在需要处理大量数据或创建复杂数据结构时非常有用。 支持算术运算指针支持算术运算如加法、减法和比较这使得它们在处理数组和内存块时非常有用。 风险更高由于指针的灵活性它们也更容易出错。例如悬挂指针、野指针、内存泄漏和缓冲区溢出等问题通常与指针的使用有关。
选择指针还是引用 如果你需要一个必须被初始化的变量别名使用引用。引用在创建时必须被初始化且之后不能改变指向这有助于避免一些常见的错误。 如果你需要处理可能为空的情况使用指针。引用不能为空而指针可以。 如果你需要动态分配内存使用指针。虽然可以使用引用来引用动态分配的对象但指针在内存管理方面提供了更多的灵活性。 如果你需要处理数组或内存块使用指针。指针支持算术运算这使得它们在处理数组和内存块时非常有用。 如果你希望代码更加简洁和直观在可能的情况下使用引用。引用的语法更接近于普通变量这使得代码更易于阅读和理解。 如果你需要传递大型对象考虑使用引用或指针来避免不必要的复制。然而请注意对于小型对象复制可能更高效因为复制的开销可能小于传递指针或引用的开销包括可能的间接访问和缓存未命中。
总的来说选择使用指针还是引用取决于你的具体需求。在可能的情况下优先考虑使用引用因为它们提供了更好的安全性和语法简洁性。然而在某些情况下指针的灵活性和动态内存管理能力可能是必要的。 PS:这些都是浮云代码能跑就行