2013-04-24 44 views
60

我在这里做了一个测试,但输出是一个没有结束的循环,我不知道为什么。构造函数中的无限循环没有for或while

其实,我正在做另一个测试,但是当我写这个时,我不明白循环是怎么发生的。它反复输出“ABC”。

#include <map> 
#include <string> 
#include <iostream> 

class test 
{ 
public: 
    std::map <int, int> _b; 
    test(); 
    test (std::map<int, int> & im); 
    ~test(); 
    }; 

test::test() 
{ 
    std::cout<<"abc"; 
    _b.clear(); 
    _b[1]=1; 
    test(_b); 
} 

test::test(std::map <int, int>& im) 
{ 
    std::cout<<im[1]; 
} 

test::~test() {}; 

int main() 
{ 
    test a; 
} 
+1

由于'test(_b)'的递归;'但我不确定原因。 – Roddy 2013-04-24 20:07:08

+0

@ Roddy-我只是想通了;详情请参阅我的答案。 – templatetypedef 2013-04-24 20:09:14

+1

已删除不重要的内容的清理版本:http://ideone.com/z0yc7Q – Yakk 2013-04-24 20:15:33

回答

94

这里的问题是,编译器解释

test(_b); 

还不如代码创建类型test传递参数_b的临时对象,而是作为一个名为test_b变量声明一个变量,使用默认的构造函数。因此,看起来像使用第二个构造函数创建临时对象test的一段代码是递归创建test类型的新对象并再次调用构造函数。

为了解决这个问题,你可以给变量一个明确的名称,如

test t(_b); 

这只能解释为命名ttest类型的变量,使用第二个构造函数初始化。

我有从来没有之前看过这个,我已经在C++编程多年。感谢您向我展示另一个该语言的角落案例!

有关官方解释:根据C++ 03 ISO规范,§ 6。8:

有一个在语法的歧义涉及表达语句和声明:表达式语句与函数式明确的类型转换(5.2.3),作为其最左边的子表达式可以是不可区分的声明,其中所述第一声明符开始以(在这些情况下该语句是一个声明。

(我的强调)。换句话说,任何时候C++可以解释一个语句作为是表达式(临时目标铸)或作为变量声明,它将选择声明。C++规范明确给出了

T(a);

作为声明的示例,而不是将a转换为T类型的内容。

这是C++的Most Vexing Parse - 看起来像一个表达式反而被解释为声明。我以前看过MVP,但在这方面我从来没有见过。

希望这会有所帮助!

+1

谢谢,这对于Google来说确实很难。 – Antimony 2013-04-24 20:11:26

+0

很好的答案!任何规范参考可用于证明/反驳编译器正确解释这一点? FWIW,codepad.org表现出相同的行为 – Tom 2013-04-24 20:11:33

+0

啊,我找到了一个解释。它被称为“最痛苦的解析”。顺便说一下,在C++ 11中,您可以通过将其更改为'test {_b}'来解决此问题。 (使用大括号而不是括号)http://en.wikipedia.org/wiki/Most_vexing_parse – Antimony 2013-04-24 20:16:03

0

问题从构造你再次调用构造器测试(_B)

测试::测试(){性病::法院< < “ABC”; _ b.clear(); _b [1] = 1;测试(_B);}

这里是发生了什么

每次你打电话测试(_B)首先调用默认的构造函数测试::测试,它在原来的呼叫测试(_b)和循环继续,直到堆栈溢出。

删除默认的构造函数

+0

这是为什么调用默认构造函数?我想这会调用隐式转换构造函数'test :: test(std :: map &)',因为它显式传入'_b'。 – templatetypedef 2013-04-24 19:59:38

+0

它为什么调用默认的构造函数? C++不会链构造函数。 – Antimony 2013-04-24 19:59:54

+0

在C++中,你不应该调用另一个构造函数。例如,这与Java不同。 – OlivierD 2013-04-24 20:00:19

0

测试(_B)我不熟悉标准的特殊性,但它可能是调用构造函数内的构造是不确定的。因此它可能是编译器依赖的。在这种特殊情况下,它会导致无限递归您的默认构造函数,而无需使用map参数调用您的构造函数。

C++ FAQ 10.3有一个带有两个参数的构造函数的例子。如果向第二个构造函数(如test(map, int))添加int参数,它会表现出某种正常行为。

对于好的表单,我会简单地将test::test(std::map <int, int>& im)更改为test::testInit(std::map <int, int>& im)test(_b)testInit(_b)

+0

它调用另一个构造函数,但它不像以前一样。我不明白为什么会导致这里展示的无限递归。 – templatetypedef 2013-04-24 20:02:30

0

我敢肯定,你实际上并不是“调用构造函数”,因为它们不是直接可调用的IIRC。法律语言必须与建设者不是命名功能 - 我没有一个标准的方便副本,或者我可以引用它。我相信你在做什么test(_b)创建一个未命名的临时调用默认的构造函数。

+0

它不创建一个未命名的临时文件并调用默认构造函数;相反,它创建一个名为'_b'的变量并调用它的默认构造函数。查看我的答案了解更多详情。 – templatetypedef 2013-04-24 20:10:05

+0

谢谢。我记得它做的不是确切的原因。我忘记了C/C++声明规则是如何扭曲的。 – 2013-04-24 20:11:19