2012-07-12 57 views
1

程序的輸出下面是C++迭代難題

1: foo strlen: 3 
2: strlen: 0 
3: foo strlen: 3 
4: foo strlen: 3 
5: strlen: 0 
6: strlen: 0 

我不明白

  • 爲什麼1輸出字符串,但2不和
  • 什麼之間的區別三個環是

來源:

#include "stdafx.h" 
#include <map> 
#include <string> 

using namespace std; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    map<string, string> m; 
    m["foo"] = "bar"; 

    const char * s; 

    for(map<string, string>::iterator it = m.begin(); it != m.end(); it++) 
    { 
     pair<string, string> kvPair = *it; 
     s = kvPair.first.c_str(); 
     printf("1: %s strlen: %d\n", s, strlen(s)); 
     break; 
    } 
    printf("2: %s strlen: %d\n", s, strlen(s)); 

    for(map<string, string>::iterator it = m.begin(); it != m.end(); it++) 
    { 
     s = (*it).first.c_str(); 
     printf("3: %s strlen: %d\n", s, strlen(s)); 
     break; 
    } 
    printf("4: %s strlen: %d\n", s, strlen(s)); 

    for(map<string, string>::iterator it = m.begin(); it != m.end(); it++) 
    { 
     s = ((pair<string, string>) (*it)).first.c_str(); 
     printf("5: %s strlen: %d\n", s, strlen(s)); 
     break; 
    } 
    printf("6: %s strlen: %d\n", s, strlen(s)); 

    return 0; 
} 

更新與小C程序員的解釋++背景,將不勝感激。

+0

Nitpick>:「stdafx.h」,缺少,缺少'std ::'。 ;-) – DevSolar 2012-07-12 06:25:13

+1

是不是'kvPair.first.c_str()'臨時? – DevSolar 2012-07-12 06:26:50

+0

就像它來自的「kvPair」一樣。 – 2012-07-12 06:27:43

回答

3

部分偶然。

在1/2您在循環中創建一個局部變量,複製值 縮小地圖到kvPair。您將s設置爲指向此 副本中的數據。當您退出 塊時,副本被銷燬(析構函數調用)。以任何方式:breakgoto,異常,或者簡單地整理 循環體,並通過它再次—每次通過 循環下去,你會得到一個新的kvPair,這是在循環 體結束破壞。 s指向kvPair.first之內的數據,以及任何使用s (即使是簡單地複製它)在kvPair之後被破壞的行爲都是未定義的 行爲。任何事情都可能發生,並且根據調試檢查和優化的級別,或者甚至取決於程序的完全不相關的方面,發生的情況可能是 。 (如果你 始終得到一個空字符串,有可能是一些設計不良 調試檢查回事。精心設計的調試檢查將導致 立即崩潰,所以你會看到錯誤。)

在2/3,您使用地圖的實際內容初始化s,因此在地圖遭到破壞或從 地圖中刪除該元素之前,它的有效性爲 。

在4/5,你創建一個臨時的:T(initialization)構建一個 臨時T型的,使用給出的初始化,對於任何類型的T, 包括std::pair<std::string, std::string>型。 (這不完全是 是真;如果T是參考,例如,行爲是不同的。) 然後您初始化s以指向此臨時數據。 臨時生命週期僅在 包含它的完整表達式的末尾,因此s的內容在以分號 結束語句(在本例中爲完整 表達式的結尾)變爲無效。與1/2相同,在此之後使用s 時會出現未定義的行爲。

+0

因此,如果我理解正確,將'* it'存儲在局部變量中,而不是直接使用la'(* it)',將創建1)對的實例,以及2)完整副本(而不僅僅是指針)的兩個成員字符串,「第一」和「第二」。最後,'c_str()'方法不會創建任何新的東西,而只是返回指向它所調用的字符串中的某些東西的指針,只要該字符串無效,該指針就是無效指針。這是對的嗎? – 2012-07-12 08:18:34

+0

@EugeneBeresovksy差不多。通常,C++使用_value_語義;新變量,臨時等等是原始的_copies_,並且兩個對象是完全不同的。 (使用引用時例外)但是'c_str()'_does_創建一個新對象。然而,一個'char const *'類型的對象;在C++中,指針是對象。 – 2012-07-12 09:17:14

+0

所以c_str()創建一個新的指針,但是它是一個指向字符串值中某個地方的指針,一旦它超出了作用域就會被回收。我想我需要習慣變種1複製這一對,而變種2不能,並直接訪問地圖對(如果我在這裏假設正確)。那麼這對我來說都是有意義的。 – 2012-07-12 15:31:21

5

在你的第一個例子,你叫c_str()kvPair這是在for -loop的範圍內聲明。退出for循環時結果無效,因爲kvPair已被銷燬。

在第二個示例中,您可以在地圖上的值上調用c_str()。當地圖被銷燬時,結果纔會失效,當_tmain(...)返回時會發生這種情況。

在第三個示例中,您將臨時調用c_str()(由對轉換創建),並且在調用printf("5...之前該臨時被銷燬。

說明

通過c_str()點由string它被稱爲上擁有一些內存返回的指針,所以當string被破壞,訪問指針是未定義的行爲。

+0

您是否願意分解正在發生的事情(對於C++學習者)?因爲即使'kvPair'是臨時的(因爲它是在for循環中聲明的),爲什麼'kvPair.first.c_str()'的結果是臨時的,當存儲在's'中時,它被聲明爲for循環? – 2012-07-12 07:07:12

+1

雖然很清楚你的意思,用C++的說法,'kvPair'不是一個臨時的;術語「臨時」保留給在完整表達式結尾處析構的未命名對象(如他在第三個循環中使用的對象)。 – 2012-07-12 07:39:02

+0

@JamesKanze:你說得對,我會試着重述。 – Kleist 2012-07-12 12:03:10

0

第一循環s指向在對數據 - 對超出範圍的循環之後,讓你的數據是僞造的

第二和第三回路有s指向數據的實際收集 - 所以它保持在範圍內

+1

第三個循環有's'指向一個臨時的,它在完整表達式的末尾被破壞。 – 2012-07-12 07:43:25