6

我一定对 C++11 有一个根本性的误解。我的教授告诉我,除了通过引用或指针之外,不可能将非原始类型传递给函数。但是,以下代码可以正常工作

#include <iostream>
using namespace std;

class MyClass
{
public: 
    int field1;
};

void print_string(string s) { 
    cout << s << endl; 
}

void print_myclass(MyClass c) { 
    cout << c.field1 << endl; 
}

int main(int argc, char *argv[]) 
{
    string mystr("this is my string"); 
    print_string(mystr); // works
    MyClass m; 
    m.field1=9; 
    print_myclass(m); 
    return 0;
}

运行程序会产生以下输出

this is my string
9

RUN SUCCESSFUL (total time: 67ms)

我在 Win7 上使用 MinGW/g++

为什么这行得通?我认为非原始类型不能按值传递?!

4

3 回答 3

9

非原始类型当然可以按值传递。[expr.call](这在 C++ 标准的第 5.2.2 节中有介绍。)

但是,有几个原因经常不鼓励这样做,尤其是在 C++03 代码中。

首先,对于大型对象,这样做的效率较低(与通过引用传递相比),因为数据是在堆栈上传递的。引用将在堆栈上占用一个单词,因此通过堆栈传递大于一个单词的任何对象必然会更慢。

其次,按值传递调用复制构造函数(或者,正如@templatetypedef 指出的那样,可能是 C++11 中的移动构造函数)。这种额外的处理可能会产生一定的开销。

第三,您可能打算修改传入的对象,但是通过传入一个副本(按值),您在函数中所做的任何更改都不会影响原始对象。因此,确保语义正确(即是否要修改原件)非常重要。因此,在某些情况下,这是一个潜在的错误。

最后,如果有一个写得不好的类没有复制构造函数或赋值运算符,编译器会自动为你生成一个默认的。这将执行浅拷贝,这可能会导致内存泄漏等问题。这是实现这些特殊方法非常重要的另一个很好的理由。完整的细节在这篇文章中:

一般来说,对于 C++03 代码,const&如果您不打算修改对象,通常会通过引用传递,&如果您需要修改对象,则通常会通过普通引用传递。如果参数是可选的,则使用指针。

在这些问题中也找到了一些很好的答案和讨论,尤其是关于移动语义的讨论:

C++11 的完整答案更复杂:

可能是使用哪种方法的最佳总结:

于 2013-08-21T00:42:30.207 回答
3

你的教授完全错了,也许他在考虑 JAVA 或 C#?在 C++ 中,一切都是按值传递的。要通过引用传递某些内容,您需要使用 & 修饰符传递它。

于 2013-08-21T00:42:02.690 回答
1

非原始类型确实可以在 C++ 中按值传递。如果您尝试这样做,C++ 将使用称为复制构造函数的特殊函数(或在 C++11 中的某些情况下,移动构造函数)将参数初始化为参数的副本。众所周知,编写复制构造函数和赋值运算符是 C++ 的一个棘手部分(出错很容易,而正确则很难),因此教授可能试图阻止您这样做。未能编写复制构造函数或这样做不正确很容易导致程序崩溃,并且是新 C++ 程序员的常见混淆源。

我建议在 Google 上搜索“C++ Rule of 3”或“复制构造函数赋值运算符”,以了解有关如何编写智能复制对象的函数的更多信息。了解如何做到这一点需要一点时间,但是一旦你理解了这些概念,这并不难。

希望这可以帮助!

于 2013-08-21T00:41:59.820 回答