Void函数接受引用与在C ++中返回值的函数

时间:2013-12-07 08:00:47

标签: c++ initialization

在C ++中,通过将引用传递给“初始化”函数来初始化变量是一种好习惯吗?或者,换句话说,最好是编写以这种方式运行的函数(即更新变量在其他地方创建)?在我的介绍编程类(用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;
}

3 个答案:

答案 0 :(得分:2)

初始化引用样式过去常用于在C ++ 98中初始化复杂数据类型,它不提供移动构造函数,并且返回值优化尚未普遍实现。

例如,创建并返回大型向量的函数会皱眉,因为它实际上会创建一个临时向量,将(缺少可靠地实现RVO的编译器)复制到目标向量,以及它的所有元素。这种不必要的本地分配和复制导致一些程序员和样式指南推荐按参考样式进行初始化。现代C ++使用移动构造函数和std::move来解决此抱怨,因此可以停用按引用模式进行的初始化。

答案 1 :(得分:1)

第二个选项过去流行的原因是由于昂贵的复制对象(例如std::stringstd::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,而是在每次迭代时都会导致堆分配,我们可以在开始时简单地进行一次堆分配并继续重用相同的空间。

答案 2 :(得分:0)

通过引用传递使得代码的可读性降低 - 代码读取的次数比写入的更多。

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程序员。)