当构造函数在VS2013中调用自身时会发生什么?

时间:2019-01-14 07:13:58

标签: c++ visual-studio-2013 visual-studio-2017

 public IViewComponentResult Invoke(FurnitureType furnitureType, bool free)
    {
        List<Furniture> furniture = _context.Furniture.Where(x => x.FurnitureType == furnitureType).ToList();
        return View(furniture);
    }
  1. 很显然,类A的构造函数会自行调用。
  2. “静态A a”将陷入循环。
  3. 在VS2013上,此代码可以退出循环并在控制台上打印“ End”。
  4. 在VS2017上,此代码陷入循环。

    ** VS2013对此代码做了什么?

2 个答案:

答案 0 :(得分:18)

没有特别的事情发生。根据C ++标准:

  

[stmt.dcl] (强调我的意思)

     

4动态初始化带有静态存储的块范围变量   持续时间或线程存储持续时间是第一次执行   控制通过其声明;这样的变量被认为   初始化完成后初始化。如果   初始化通过抛出异常退出,初始化是   尚未完成,因此下次进入控件时将再次尝试   声明。如果控件同时输入声明   变量正在初始化,并发执行应等待   完成初始化。 如果控件重新输入   在初始化变量时递归声明,   行为未定义。 [示例

int foo(int i) {
  static int s = foo(2*i);      // recursive call - undefined
  return i+1;
}
     

最终示例]

我胆怯的语句正是您的程序中发生的事情。这也是标准示例显示为未定义的内容。语言规范指出,实现可以执行其认为适当的任何操作。因此,这可能会导致无限循环,也可能不会导致无限循环,具体取决于您的实现用来防止并发重新进入该块的同步原语(初始化必须是线程安全的。)

即使在C ++ 11之前,递归重入的行为也是不确定的。一个实现可以做任何事情来确保一个对象仅被初始化一次,从而可能产生不同的结果。

但是您不能期望任何特定的事情会发生。更不用说不确定的行为总会给小小的鼻恶魔留下空间。

答案 1 :(得分:4)

该行为是不确定的。它在Visual Studio 2013中“起作用”的原因是它没有实现函数静态函数的线程安全初始化。可能发生的情况是,对GetA()的第一次调用创建了a并调用了构造函数。然后,对GetA()的第二次调用仅返回部分构造的a。由于构造函数的主体不会初始化,因此调用Print()的任何操作都不会崩溃。

Visual Studio 2017确实实现了线程安全初始化。并且如果GetA()尚未初始化,则可能在进入a时锁定了一些互斥锁,因此对GetA()的第二次调用会遇到锁定的互斥锁和死锁。

请注意,在这两种情况下,这只是我从观察到的行为中得出的猜测,实际行为是不确定的,例如,GetA()可能最终会创建两个A实例。