当构造函数在VS2013中调用自身时会发生什么?
What happens when a constructor function calls itself in VS2013?
class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();
class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}
IA* GetA()
{
static A a;
return &a;
}
int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}
- 显然,类 A 的构造函数调用自身。
- "静态 A a"将卡在循环中。
- 在VS2013上,此代码可以从循环中出来并在控制台上打印"End"。
在VS2017上,此代码陷入循环。
**VS2013对此代码有什么作用?
没有什么特别的事情必须发生。根据C++标准:
[stmt.dcl](强调我的)
4 使用静态存储动态初始化块范围变量 持续时间或线程存储持续时间在第一次执行 控制通过其声明;考虑这样的变量 初始化完成后初始化。如果 初始化通过抛出异常退出,初始化为 不完整,因此下次控件进入时将再次尝试 宣言。如果控件同时输入声明,而 变量正在初始化,并发执行应等待 以完成初始化。如果控件重新进入 在初始化变量时递归声明, 行为未定义。[示例:
int foo(int i) { static int s = foo(2*i); // recursive call - undefined return i+1; }
—结束示例]
我大胆的陈述正是您的程序中发生的事情。这也是标准示例显示为未定义的内容。语言规范说,实现可以做任何它认为合适的事情。因此,它可能会导致无限循环,也可能不会导致无限循环,具体取决于您的实现用于防止并发重新进入块的同步原语(初始化必须是线程安全的)。
甚至在C++11之前,递归再入的行为也是不确定的。实现可以做任何事情来确保对象只初始化一次,这反过来又会产生不同的结果。
但是你不能指望任何特定的事情会随身携带。更不用说未定义的行为总是为鼻魔的小机会留下空间。
行为未定义。它在Visual Studio 2013中"工作"的原因是它没有实现函数静态的线程安全初始化。可能发生的情况是,对GetA()
的第一个调用会创建a
并调用构造函数。然后,对GetA()
的第二次调用仅返回部分构造的a
。由于构造函数的主体不初始化任何调用Print()
不会崩溃。
Visual Studio 2017 确实实现了线程安全初始化。 并且可能会在进入GetA()
时锁定一些互斥锁,如果a
未初始化,则对GetA()
的第二次调用会遇到锁定的互斥锁和死锁。
请注意,在这两种情况下,这只是我对观察到的行为的猜测,实际行为是未定义的,例如GetA()
最终可能会创建 2 个A
实例。
- 是什么让放置新调用对象的构造函数?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- 使用基类指针调用基类的值构造函数的语法是什么?
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- 构造函数后面的宏.什么意思?
- C++构造函数内部会发生什么?
- 用相同的参数声明两个构造函数的最偶像化的方法是什么?
- 如果在 C++ 构造函数中以错误的顺序初始化对象数据,会发生什么类型的错误
- 我是否为邪恶刽子手的构造函数错过了什么?
- 构造函数中没有参数的对象类成员按什么顺序初始化?
- 什么时候用指针调用C++类构造函数
- 这个构造函数是做什么的
- 将复制构造函数设置为private和=delete有什么区别
- 我不明白在这个例子中什么时候调用构造函数
- 在C++中,从构造函数中将字符串文本分配给成员const char*变量时会发生什么
- c++ 17 中结构自动定义构造函数的规则是什么?
- 在C++中初始化带有和不使用构造函数调用的对象有什么区别
- 指示汇编时间叫什么构造函数
- 在这种情况下调用什么构造函数
- 使用什么构造函数