2012-07-12 58 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