c ++中块内局部变量的存储分配

时间:2011-06-30 16:23:41

标签: c++ constructor

我想知道编译器在哪一点为块内的局部变量分配存储空间。 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’

6 个答案:

答案 0 :(得分:13)

6.7 / 3:

  

可以转入   阻止,但不是绕过的方式   初始化声明。一个   从一个点跳过的程序   具有自动的局部变量   存储持续时间不在范围内   它在范围内的点是   除非变量具有POD,否则形成不良   类型(3.9)并声明没有   初始化程序(8.5)。

在分配存储时,但在调用构造函数时,重要的不是。跳过构造函数的goto将是一个问题,这就是它被禁止的原因。 (没有初始化器的POD类型不需要任何构造,因此允许它们。)

答案 1 :(得分:4)

问题的第一部分很简单:大多数编译器将所有本地分配整理成单个堆栈分配,然后对该分配进行分区。初始化只有在它们进入范围或它们被明确初始化时才会发生。

从编码的角度来看,你的例子非常糟糕,因为你跳过x进入范围的点,因此永远不会调用构造函数(这是{{1}的原因之一是错误)以及为什么你的编译器告诉你不要试图滥用它。但是,某些类型可以保持未初始化状态,例如gotoint等内置类型,您将收到警告,这就是为什么如果跳过跳过,一切都会抛出错误的原因它的初始化(构造函数)。

答案 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’
$

每个变量xx2x3都有默认构造函数。

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)

您不能跨越构造函数gotocase。 Builtins没有构造函数。

编译器知道它何时进入函数的局部变量的总空间要求是什么,它将移动堆栈指针以容纳它们。这段记忆没有被初始化。

它在函数流中调用构造函数和析构函数。这就是为什么你不能像这样使用gotocase - 它打破了不变量。诸如break之类的语句在必要时调用析构函数,例如for循环,一切正常。

答案 5 :(得分:0)

我来到这里是因为我有同样的问题。
对我有帮助的是在case之外判断'树'。
而不是Tree t2 (45);,而是Tree t2;之前放置switch t2 (45);内的case 1

然后你需要为Tree t3 (45);做同样的事情。