我想知道编译器在哪一点为块内的局部变量分配存储空间。 goto和switch如何跳过构造函数? :
class Tree {/*...*/}
...
void foo (int i){
if (i < 10) goto label; //illegal: cannot jump past a ctor
Tree t (45);
label:
switch (i){
case 1:
Tree t2 (45);
break;
case 2: //illegal: cannot jump past ctor
Tree t3 (45);
break;
}
}
虽然上面的代码不适用于用户定义的对象,但如果我用内置对象替换它们,它就可以工作。那是为什么?
编辑: 内置int,char等对象 我得到的错误(关于ubuntu的g ++ 4.5):
jumpPastConstructor.c++: In function ‘void foo(int)’:
jumpPastConstructor.c++:26:3: error: jump to label ‘label’
jumpPastConstructor.c++:24:20: error: from here
jumpPastConstructor.c++:25:10: error: crosses initialization of ‘Tree t’
jumpPastConstructor.c++:31:16: error: jump to case label
jumpPastConstructor.c++:29:25: error: crosses initialization of ‘Tree t2’
答案 0 :(得分:13)
6.7 / 3:
可以转入 阻止,但不是绕过的方式 初始化声明。一个 从一个点跳过的程序 具有自动的局部变量 存储持续时间不在范围内 它在范围内的点是 除非变量具有POD,否则形成不良 类型(3.9)并声明没有 初始化程序(8.5)。
在分配存储时,但在调用构造函数时,重要的不是。跳过构造函数的goto将是一个问题,这就是它被禁止的原因。 (没有初始化器的POD类型不需要任何构造,因此允许它们。)
答案 1 :(得分:4)
问题的第一部分很简单:大多数编译器将所有本地分配整理成单个堆栈分配,然后对该分配进行分区。初始化只有在它们进入范围或它们被明确初始化时才会发生。
从编码的角度来看,你的例子非常糟糕,因为你跳过x
进入范围的点,因此永远不会调用构造函数(这是{{1}的原因之一是错误)以及为什么你的编译器告诉你不要试图滥用它。但是,某些类型可以保持未初始化状态,例如goto
,int
等内置类型,您将收到警告,这就是为什么如果跳过跳过,一切都会抛出错误的原因它的初始化(构造函数)。
答案 2 :(得分:3)
在xx.cpp中转换为可编译代码:
class C
{
int i;
public:
C(int i_val = 0) : i(i_val) { }
};
int main()
{
int someval = 2;
goto label; //error
C x;
label:
switch (someval)
{
case 1:
C x2;
break;
case 2: //error
C x3;
break;
}
}
并在MacOS X 10.6.8上用G ++ 4.6.0编译,产生显示的错误:
$ g++ -Wall -Wextra -c xx.cpp
xx.cpp: In function ‘int main()’:
xx.cpp:13:1: error: jump to label ‘label’ [-fpermissive]
xx.cpp:11:10: error: from here [-fpermissive]
xx.cpp:12:7: error: crosses initialization of ‘C x’
xx.cpp:19:14: error: jump to case label [-fpermissive]
xx.cpp:17:15: error: crosses initialization of ‘C x2’
$
每个变量x
,x2
和x3
都有默认构造函数。
C ++标准只是说你不允许跳过变量构造的块。有用的是:
class C
{
int i;
public:
C(int i_val = 0) : i(i_val) { }
};
int main()
{
int someval = 2;
goto label; //error
{
C x;
}
label:
switch (someval)
{
case 1:
{
C x2;
}
break;
case 2: //error
{
C x3;
}
break;
}
}
使用三对额外的大括号,您不再跳转到声明和初始化变量的块,因此代码是合法的,并在之前显示的命令行下干净地编译。
答案 3 :(得分:1)
此处的解决方案是:为每个 CASE
添加括号switch (i){
case 1:{
Tree t2 (45);
break;
}
case 2: {//illegal: cannot jump past ctor
Tree t3 (45);
break;
}
}
我不知道这是多么疯狂!!!!但是添加{和}可以解决这个问题!
答案 4 :(得分:0)
您不能跨越构造函数goto
或case
。 Builtins没有构造函数。
编译器知道它何时进入函数的局部变量的总空间要求是什么,它将移动堆栈指针以容纳它们。这段记忆没有被初始化。
它在函数流中调用构造函数和析构函数。这就是为什么你不能像这样使用goto
或case
- 它打破了不变量。诸如break
之类的语句在必要时调用析构函数,例如for循环,一切正常。
答案 5 :(得分:0)
我来到这里是因为我有同样的问题。
对我有帮助的是在case
之外判断'树'。
而不是Tree t2 (45);
,而是Tree t2;
之前放置switch
t2 (45);
内的case 1
。
然后你需要为Tree t3 (45);
做同样的事情。