使用引用的Void函数与在C++中返回值的函数相比
Void functions that take a reference vs functions that return a value in C++
在C++中,通过将对变量的引用传递到"initialization"函数来初始化变量是一种好的做法吗?或者,换言之,编写以这种方式运行的函数(即更新在其他地方创建的变量)是一种很好的做法?在我的编程入门课(用Java教授)中,我们被教导将这样的方法写成static
,并给它们显式的返回值。但我从一些示例中注意到,一些C++程序员在没有显式初始化的情况下声明他们的变量,将它们交给某个函数,然后继续在程序中使用它们。这两种风格都有优点/缺点吗?(我把纯粹OO的东西,比如成员函数和变量排除在这个问题之外——这不仅仅是关于使用方法更新对象的状态。我在C++中看到过在类之外这样做)。
我写了几行快速的代码来说明我的意思。第一个函数genName()
是我熟悉的样式。第二,gen_name()
是我很好奇的那种。
string genName() {
string s = "Jack" ;
return s ;
}
void gen_name(string & s) {
s = "Jill" ;
}
int main(int argc, const char * argv[]) {
string name1 = genName() ;
string name2 ;
gen_name(name2) ;
cout << name1 << endl ;
cout << name2 << endl ;
return 0;
}
在C++98中,引用初始化风格曾经流行于初始化复杂的数据类型,因为C++98没有提供移动构造函数,并且返回值优化尚未普遍实现。
例如,创建并返回大向量的函数会受到反对,因为它实际上会创建一个临时向量,该向量(缺乏可靠实现RVO的编译器)会被复制到目标向量及其所有元素。这种不必要的本地分配和复制导致一些程序员和样式指南建议处处使用引用样式进行初始化。现代C++通过move构造函数和std::move
解决了这一问题,因此引用模式的初始化可以退役。
第二个选项之所以流行,是因为复制对象(如std::string
、std::map
等)的开销很高。如果复制这些对象,不仅会有深度复制元素的开销,还会有代价高昂的堆分配开销。
话虽如此,在C++11中,由于移动语义,很多这样的东西都消失了,它允许我们做一些以前做不到的事情。
例如,如果您希望name
是一个const
对象,这可能很有用。
const std::string name = []() {
std::string name;
/* Fill in name. */
return name;
}();
但是,请注意,在某些情况下,通过引用进行初始化仍然很有用。例如,以下代码:
for (int i = 0; i < N; ++i) {
const std::string name = gen_name(i);
/* Use name here. */
} // for
尽管如果我们知道不会修改const
,那么添加它会很好,但就性能而言,以下操作会更快。
std::string name;
for (int i = 0; i < N; ++i) {
gen_name(i, name);
/* Use name here. */
} // for
编辑:
我之所以指出,在某些情况下,通过引用进行初始化可能更可取,是因为有时我们可以重用在循环中获得的资源。在上面的例子中,我们可以简单地在开始时进行单个堆分配,并保持重用相同的空间,而不是在每次迭代中构建一个新的实例std::string
,这将导致每次迭代中的堆分配。
通过引用传递会降低代码的可读性,而且代码读起来比写起来更难。
C++中最重要的原因是,在Java中,每个对象都是引用,所以结果很便宜。在C++中,可能会返回整个结构。少量的复制开销。
但参考参数也有好处:
多个结果否则需要一个额外的结果类型作为多个结果值的容器。
void divideAndRemainder(int p, int q, int& d, int& r)
多个结果也需要准备输入。
void swapVariables(int& a, int& b);
字段别名填充该字段或该字段/变量。
struct link {
struct link* next;
int value;
}
// Ordered list insert, not possible like this in Java:
// Read "struct link*&", but for clarity I use explicit dereferencing here:
// *list.
void insert(struct link** list, int value) {
while (*list && value < (*list)->value) {
list = &(*list)->next;
}
struct link* next = *list;
*list = new struct link();
(*list)->value = value;
(*list)->next = next;
}
(注意,我现在是一个根深蒂固的Java程序员。)
- 程序不向函数返回值
- 如何将不可移动和不可复制的函数返回值获取到数组中
- 存储函数返回值或立即使用c++
- 如何删除新分配的字符,这也是函数返回值?
- 不从函数返回值会导致段错误
- lambda 函数返回值的有效性
- 如何获取有关 Visual Studio 代码分析中未经检查的函数返回值的警告
- 存储指向函数返回值的指针
- 在 OOP 中,调用函数返回值还是直接调用值更快?
- 函数返回值的自动类型扣除
- 为循环中多次调用的函数返回值预分配内存的正确方法是什么
- 函数返回值的位运算符提升
- 从捕获 constexpr 函数返回值的变量中删除 constexpr 会删除编译时计算
- C++ ReSharper - 生成函数返回值/参数类型的变量
- std::clamp - 检测函数返回值是否绑定到 const T&
- C 递归函数返回值
- 是否可以在C 中使回调函数返回值
- C++标准是否保证函数返回值具有常量地址?
- 将函数返回值传递给引用参数会导致 GCC 中的编译错误
- 函数返回值错误