2011-11-02 89 views
3

我在教自己C++,在这个过程中我正在编写简单的小程序来学习基本思想。对于“传递按引用”,我很困惑,为什么下面的代码段工程(部分代码就在那里练习重载构造函数):C++:使用&运算符通过引用

#include <iostream> 
#include <string> 
using namespace std; 

class Dude 
{ 
public: 
    string x; 
    Dude();     // Constructor 1 
    Dude(const string &a);  // Constructor 2 
}; 

Dude::Dude() : x("hi") {} 
Dude::Dude(const string &a) : x(a) {} 

int main() 
{ 
    Dude d1; 
    Dude d2 = Dude("bye"); 

    cout << d1.x << endl; 
    cout << d2.x << endl; 

    return 0; 
} 

在“主要()”,我创建了一个类型为“Dude”的对象“d2”,并使用构造函数2将“x”设置为字符串“bye”。

但是在构造函数2的声明中,我告诉它接受一个字符串的地址,而不是字符串本身。那么为什么我可以通过它“再见”(这是一个字符串)。为什么我不需要创建一个变量字符串,然后将该字符串的地址传递给Dude的构造函数2?

+0

谁说基准参数是指传递一个地址?他们必须执行。 – delnan

+0

尽管参考并不是C++所关心的地址,但在现实生活中,它几乎总是以这种方式实现。但是引用根本不像指针。好吧,几乎没有。 –

+0

我改变了标题以更好地反映问题/答案。 – higgy

回答

5

这实际上说明了C++最酷和最有用的特性之一:临时变量。由于您指定字符串引用为const,因此编译器允许您将引用传递给该函数的临时值。所以,这里是发生了什么幕后与Dude d2 = Dude("bye");

  • 编译器确定使用最好的构造是Dude::Dude(const string &)。如何做出这个选择是一个完全不同的话题。
  • 但是,为了使用该构造函数,您需要一个string值。现在,"bye"const char[4],但编译器可以简单地将其转换为const char *,而可以将转换为string。因此,创建了一个匿名临时变量(称为temp1)。
  • string::string(const char *)被调用,"bye",并且结果被存储在temp1
  • Dude::Dude(const string&)被调用,以temp1的参考。结果被分配到d2(实际上,它被分配给另一个临时变量,Dude的拷贝构造函数被调用时使用const引用,而被赋值给d2,但在这种情况下结果是相同的。
  • temp1被丢弃。这是字符串的析构函数string::~string()temp1
  • 控制运行传递给下一个语句
+3

“bye”不是'const char *',它是'const char [4]'。它*衰减*到上述上下文中的指针。 – Praetorian

+0

这是一个微妙而重要的区别,特别是对于那些真正想知道类型系统如何工作的人来说。回答编辑包括。 –

1

两件事:

1)代码中没有“地址”这样的东西。 const string&的意思是“常数参考string”。

您可能会被以下事实弄糊涂:符号&也被用于与“address-of”操作符创建指针的完全不同的上下文中:T x; T * p = &x;。但是这与参考文献无关。

2)你实际上并不一定使用你要求的构造函数d2;相反,您正在使用构造函数#2创建临时对象,然后通过临时构造函数构造d2。直接建设内容为Dude d2("bye");

+0

我同意你的第一点,但也许你应该解释为什么参考*不是地址。 –

2

我认为你误解了&运营商在这方面做了什么。取一个变量的地址(&var)不同于表示一个参数将作为参考(因为您有,在const string &a)传递。

你的代码实际上做的是隐式创建一个与字符串初始化"bye",然后将该对象通过引用到Dude构造函数传递string对象。也就是说,你的代码基本上是:

Dude d2 = Dude(string("bye")); 

,然后构造通过参考接收到字符串对象,并通过拷贝构造函数把它分配给x

+0

我想我现在看到了。在函数的参数中,&运算符的含义与初始化指针时不同。此外,我还没有意识到幕后有很多事情要做。 – higgy

+0

确切地说,'&'运算符在这些上下文中有两个不同的含义。在变量前面,它需要它的地址。在查看参数时或者在声明类型之后(如:int&i = j')时,在标识符前面创建一个引用,而引用只是将新名称分配给同一个值。这个名字不能改变。 – yan

2

在这种情况下,string有一个构造函数,需要一个const char*并没有宣布explicit,所以编译器会创建一个临时string,然后你const string&(与string("bye"),上述构造函数创建)设置为指的是暂时的。

+0

有问题的人是从“再见”创建的。 –

+0

@MooingDuck哈哎,这两个词如此接近,但迄今为止。谢谢,也可以随时编辑我的答案,就像将来那样。 –

+0

我通常只有在我完全确定海报有正确的想法时才纠正拼写错误,并且在评论方面犯错。偶尔我会添加代码示例,如果它被要求和海报不足够快速添加它。 –

1

当您使用字符串参数调用第二个构造函数时,会创建一个引用该字符串副本的临时变量并将其传递给构造函数。

1

构造函数2没有获取字符串的地址,const string& a意味着对std::string对象的常量引用。为什么你可以传递构造函数一个字符串文字是因为std::string类包含一个非显式构造函数,它需要一个const char *。因此,编译你的字符串文字到std::string第一隐式转换调用构造函数2.

之前,所以写的时候构造,更喜欢在初始化列表中初始化的成员变量,而不是下面的2行是等效

Dude d2 = Dude("bye"); 
Dude d2 = Dude(std::string("bye")); 

而且,在构造函数体内

Dude(const string &a) : x(a) {} 
+0

好的,所以对于构造函数2,我会在类定义中放置一个原型Dude(const string&a),然后用Dude :: Dude(const string&a){x = a; }。 – higgy

+0

@higgy不,你仍然应该使用初始化列表。 'Dude :: Dude(const string&a):x(a){}' – Praetorian

1

temporaryly可以绑定到const引用,可能是出于这个原因。

当您调用Dude("bye")时,编译器会发现对于任何构造函数,这是否完美匹配(char[4])。不。然后它会检查某些转换(char*)仍然不可用。然后它检查用户转换,发现std::string可以隐含地从char*构建,因此它会为您创建char*中的std::string,并通过引用Dude的构造函数传递它,该构造函数会进行复制。在声明Dude d2 = Dude("bye");的末尾,临时字符串被自动销毁。如果我们必须为每一个单一的函数参数进行明确的演员表,那将是令人烦恼的。

传递给引用参数的变量会自动传递它们的地址。这很好,因为它允许我们用值语义处理对象。我不必考虑将它传递给一个字符串实例,我可以通过它"bye"

1

构造函数#2接受对const string的引用。这允许它接受对预先存在的对象的引用临时对象(没有const限定符,不会接受对临时引用的引用)。

std::string有一个接受指向char的指针的构造函数。编译器使用它来创建一个临时的std::string对象,然后将对该临时对象的引用传递给您的ctor。

请注意,编译器将只会(隐式)做一个像这样的转换给你。如果您需要进行多次转换才能从源数据获取目标类型,则需要明确指定除转换之外的所有转换。

1

虽然“&”是运算符的地址,但在作为方法定义/声明的一部分进行声明时,这意味着将引用传递给该方法。这种情况下的参考是d2。请注意,D2不是一个指针,它是一个参考。在构造函数中,“a”表示内容为“hi”的字符串对象。这是一个在C++中通过引用的方法的典型示例。