2011-11-02 51 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++中通過引用的方法的典型示例。