【问题标题】:Infinite loop in constructor without for or while没有for或while的构造函数中的无限循环
【发布时间】:2013-04-18 12:46:06
【问题描述】:

我这里做了一个测试,但是输出的是一个没有结束的循环,不知道为什么。

其实我在做另一个测试,但是当我写这个的时候,我不明白循环是怎么发生的。反复输出“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;  
}

【问题讨论】:

标签: c++ recursion constructor constants


【解决方案1】:

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

test(_b);

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

要解决此问题,您可以为变量指定一个明确的名称,例如

test t(_b);

这只能解释为名为@9​​87654332@ 的test 类型变量,使用第二个构造函数初始化。

我以前从未见过这种情况,而且我已经用 C++ 编程多年了。感谢您向我展示该语言的另一个极端案例!

官方解释:根据 C++03 ISO 规范,§6.8:

在涉及表达式语句和声明的语法中存在歧义:将函数样式显式类型转换 (5.2.3) 作为其最左边的子表达式的表达式语句与第一个声明符开头的声明无法区分a (。在这些情况下,该语句是一个声明。

(我的重点)。换句话说,只要 C++ 可以将语句解释为表达式(临时对象转换)或声明(变量),它将选择声明。 C++ 规范明确给出了

T(a);

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

这是 C++ 的 Most Vexing Parse - 看起来像表达式的东西被解释为声明。我以前见过 MVP,但我从未在这种情况下见过它。

希望这会有所帮助!

【讨论】:

  • 谢谢,这对 Google 来说太难了。
  • 好答案!任何可用于证明/反驳编译器正确解释的规范参考? FWIW,codepad.org 表现出相同的行为
  • 啊,我找到了解释。它被称为“最令人烦恼的解析”。顺便说一句,在 C++11 中,您可以通过将其更改为 test{_b} 来解决此问题。 (使用大括号代替括号)en.wikipedia.org/wiki/Most_vexing_parse
  • 检查生成的代码后,我正朝着这个方向前进,但找不到章节。当我用test(_boggle); 替换原来的test(_b); 并且它编译好并且表现和以前一样时,我有一个'啊哈!'
  • 我以前确实见过这个。在 SO 的另一个线程中被问到。
【解决方案2】:

问题出在构造函数上,你再次调用了构造函数 test(_b)

test::test(){std::cout

这就是发生的事情

每次调用 test(_b) 时,它首先调用默认构造函数 test::test ,然后依次调用 test(_b) ,循环继续进行,直到堆栈溢出。

从默认构造函数中移除 test(_b)

【讨论】:

  • 为什么要调用默认构造函数?我想这会调用隐式转换构造函数test::test(std::map&lt;int, int&gt;&amp;),因为它显式传入_b
  • 为什么会调用默认构造函数? C++ 不链接构造函数。
  • 在 C++ 中你不应该调用另一个构造函数。例如,这与 Java 不同。
  • @Oliver 是的,但这并不能解释为什么会发生循环
【解决方案3】:

我很确定您实际上并没有“调用构造函数”,因为它们不是直接可调用的 IIRC。法律术语与构造函数不是命名函数有关 - 我手边没有标准的副本,或者我可能会引用它。我相信您对 test(_b) 所做的事情是创建 an unnamed 一个临时的,它会再次调用默认构造函数。

【讨论】:

  • 它不是创建一个未命名的临时对象并调用默认构造函数;相反,它创建了一个名为_b 的变量并在其上调用默认构造函数。有关详细信息,请参阅我的答案。
  • 谢谢。我记得它在做什么只是不确切的原因。我已经忘记了 C/C++ 声明规则实际上是多么扭曲。
【解决方案4】:

我不熟悉标准的特殊性,但可能是在构造函数中调用构造函数是未定义的。因此,它可能取决于编译器。在这种特殊情况下,它会导致您的默认构造函数无限递归,而无需使用 map 参数调用您的构造函数。

C++ FAQ 10.3 有一个示例,其中的构造函数有两个参数。如果您将 int 参数添加到您的第二个构造函数,例如 test(map, int),它会表现出一些正常的行为。

为了更好的形式,我只需将test::test(std::map &lt;int, int&gt;&amp; im) 更改为test::testInit(std::map &lt;int, int&gt;&amp; im),并将test(_b) 更改为testInit(_b)

【讨论】:

  • 它确实调用了另一个构造函数,但它与以前的构造函数不同。我不明白为什么这会导致这里展示的无限递归。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-12
  • 2018-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-17
  • 1970-01-01
相关资源
最近更新 更多