2

標題說明了一切。但是,請以string作爲任何班級的佔位符。`string s(「hello」);```string s =「hello」;`

std::string s1("hello"); // construct from arguments 
std::string s2 = "hello"; // ??? 
std::string s3;   // construct with default values 
s3 = "hello";    // assign 

不知是否爲s2語句不一樣的s1s3

+1

[複製初始化和直接初始化之間在C++中是否存在差異?](http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-初始化和直接初始化) –

+0

此外:[初始化和分配](http://stackoverflow.com/questions/7350155/initialisation-and-assignment) –

回答

14

s2的情況是copy initialization。這是初始化,而不是作爲s3的情況下的分配。

注意,對於std::string,效果爲s1s2,所述apporiate構造(即std::string::string(const char*))相同的將被調用來構造對象。但是複製初始化和direct initializations1的情況)有所不同。對於複製初始化,不會考慮顯式構造函數。假設std::string::string(const char*)被宣佈爲explicit,這意味着不允許從const char*std::string的隱式轉換;那麼第二種情況將不會再次編譯。

複製初始化不如直接初始化容許:顯式構造函數不轉換構造函數,也不考慮複製初始化。

2

在這種情況下,s1s2正是同樣的事情:他們都調用構造函數來const char*。 (爲了清晰起見,一些民衆喜歡使用=)。

對於s3,默認構造函數被調用,然後operator=const char*

1

如果一個類不具有可訪問的拷貝構造函數初始化的第二種形式是無效的:

[temp]$ cat test.cpp 
struct S { 
    S(int); 
private: 
    S(const S&); 
}; 

S s1(3); // okay 
S s2 = 3; // invalid 
[temp]$ clang++ -std=gnu++1z -c test.cpp 
test.cpp:8:3: error: calling a private constructor of class 'S' 
S s2 = 3; // invalid 
^
test.cpp:4:5: note: declared private here 
    S(const S&); 
    ^
1 error generated. 
[temp]$ 

不同的是,第二個,正式,創建S類型的臨時對象,初始值爲3,然後將該臨時值複製到s2。編譯器被允許直接跳過該拷貝並構造s2,但只有在拷貝纔有效的情況下,

+0

我認爲copy ctor在這裏是不相關的。 http://melpon.org/wandbox/permlink/2VI5MmtM8qG73qTs – songyuanyao

+0

@songyuanyao - 我編輯了我的答案,以顯示編譯器輸出和開關(與您的匹配)。這仍然是一個錯誤。 –

+0

它可能是C++ 17的問題。從[這裏](http://en.cppreference.com/w/cpp/language/copy_initialization),「最後一步通常是優化出來的,並且轉換的結果直接在爲目標對象分配的內存中構建,但是相應的構造函數(move或copy)需要被訪問,即使它沒有被使用(直到C++ 17)「。 [C++ 14](http://melpon.org/wandbox/permlink/9YhwdZPPV8vIYHN0)vs [C++ 17](http://melpon.org/wandbox/permlink/VbNcgcySstUZY3RZ) – songyuanyao

2

雖然所有3種方法的最終結果是相同的(字符串將被分配給變量),但是某些特徵差異比語法更深。我將回顧3個字符串覆蓋的所有3種場景:

第一種情況:s1是直接初始化的示例。直接初始化涵蓋了許多不同的場景,您定義如下:

用非空括號表達式列表進行初始化。

這裏,S1不會有一個類數據類型,但一個std::string數據類型,所以一個標準的轉換將在數據類型在括號轉換爲S1的CV-不合格的版本,這是const *char。Cv不合格意味着沒有限定符,如附加到變量的(const)(易失性)。請注意,在直接初始化的情況下,它比s2的主題複製初始化更寬鬆。這是因爲複製初始化將只涉及用戶定義的構造函數和轉換函數,它們是非隱式(即隱式)。另一方面,直接初始化考慮隱式和顯式構造函數以及用戶定義的轉換函數。

繼續,第二個字符串s2是複製初始化的一個示例。簡而言之,它將數值從左側複製到右側。這是一個例子:

當一個命名的變量(自動,靜態或線程局部)非參考類型T的聲明與由等號的初始化符號後跟一個表達式。

該方法所涵蓋的過程是相同的。由於s2沒有類數據類型,但是數據類型爲std::string,因此它將使用標準轉換將右側字符串的值轉換爲左側的const *char類型的值。但是,如果函數是明確聲明的,則不能像複製初始化程序那樣進行標準轉換,編譯代碼將失敗。

查看與2種初始化類型相比較的代碼示例。這應該從上面清除任何混亂:

struct Exp { explicit Exp(const char*) {} }; // This function has an explicit constructor; therefore, we cannot use a copy initialization here 
    Exp e1("abc"); // Direct initialization is valid here 
    Exp e2 = "abc"; // Error, copy-initialization does not consider explicit constructor 
      
    struct Imp { Imp(const char*) {} }; // Here we have an implicit constructor; therefore, a copy initializer can be used 
    Imp i1("abc"); // Direct initialization always works 
    Imp i2 = "abc"; // Copy initialization works here due to implicit copy constructor 

移動到第三種情況,是不初始化連的情況下,它是分配的情況。就像你在你的評論中說的那樣,變量s3是用一個默認字符串初始化的。當您使用等號這樣做時,該字符串將替換爲「Hello」。這裏發生的是,當在string s3;中聲明s3時,將調用std::string的默認構造函數,並設置默認字符串值。當您使用=符號時,該默認字符串在下一行中被替換爲hello

如果我們正在研究運行時速度更快的方面,這種差異是微不足道的。然而,S1需要在最快的時間來運行,如果我們僅僅做到這一點:

int main(void) 
    { 
     string a("Hello"); 
    } 

這發生在下列時間和內存來編譯和運行:

編譯時間:0.32秒,絕對的運行時間: 0.14秒,CPU時間:0秒,內存峯值:3 MB,絕對服務時間:0,46秒

如果我們看一下字符串s2以下列方式編碼:

int main(void) 
    { 
     string a = "Hello"; 
    } 

然後採取程序運行的總時間是:

編譯時間:0.32秒,絕對運行時間:0.14秒,CPU時間:0秒,存儲器峯:3 MB,絕對服務時間:0,47秒

使用複製初始值設定項的運行時間爲0。比直接初始化器多運行01秒。差異存在,但邊際。

與S3中的第三情況下,如果編碼方式如下:

int main(void) 
    { 
     string a; 
     a = "Hello"; 
    } 

擁有總運行,編譯時間和空間考慮的:

編譯時間:0.32秒,絕對的運行時間:0.14秒,cpu時間:0秒,記憶峯值:3 Mb,絕對使用時間:0,47秒

我想在這裏指出一些東西:第二種和第三種方法最可能是零;相反,時間差小於0.01秒,第三種方法需要更長的時間(s3)。那是因爲它有兩行代碼可以操作;一個是變量聲明,另一個是字符串賦值給變量。

希望這回答你的問題。

+0

非常感謝您的詳細解答。對於你的性能測試,你應該多次重複循環中的同一步驟並計算平均時間。我相信這使得性能的差異更加清晰。此外,您必須確保編譯器不會簡單地優化未使用的字符串變量! – Fabian

+0

其實@Fabian,我多次運行過它們,但由於時間精確到只有2個小數位,從我使用的站點來看,時間是__exactly__相同!我需要的是能夠找到平均值的更精確的時間。儘管如此,感謝您的反饋:-) –

相關問題