Распределение памяти локальных переменных внутри блока в c ++ - PullRequest
6 голосов
/ 30 июня 2011

Я хочу знать, в какой момент компилятор выделяет хранилище для локальных переменных внутри блока. Как 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;
   }
}

Хотя приведенный выше код не работает для пользовательских объектов, он работает, если я заменяю их встроенными объектами. Почему это так?

Edit: Встроенные объекты, такие как int, char и т. Д. Ошибки, которые я получаю (g ++ 4.5 на Ubuntu):

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 ]

13 голосов
/ 30 июня 2011

6.7 / 3:

Можно передавать в блок, но не таким образом, чтобы обойти объявления с инициализацией.Программа, которая переходит от точки, в которой локальная переменная с автоматическим хранением находится вне области действия, до точки, в которой она находится в области действия, имеет неправильную форму, если переменная не имеет тип POD (3.9) и не объявлена ​​без инициализатора (8.5).

Важно не то, когда выделено хранилище, а когда вызывается конструктор.Goto, который перепрыгнул через конструктор, будет проблемой, поэтому он запрещен.(Типы POD без инициализатора не нуждаются в какой-либо конструкции, поэтому они разрешены.)

4 голосов
/ 30 июня 2011

первая часть вопроса проста: большинство компиляторов объединяют все локальные выделения в одно выделение стека, а затем разбивают это распределение.инициализация происходит только тогда, когда они входят в область видимости или они явно инициализируются.

Ваш пример довольно плох с точки зрения кодирования, когда вы перепрыгиваете через точку, в которой x входит в область видимости, таким образом,конструктор никогда не будет вызван (это одна из причин, по которой goto является плохой ) и почему ваш компилятор говорит вам прекратить попытки злоупотреблять им.Однако некоторые типы можно оставить неинициализированными, например, встроенные типы int, float и т. Д. Вместо этого вы получите предупреждение, поэтому не все выдает ошибку, если вы перепрыгиваете через его инициализацию (конструктор).

3 голосов
/ 30 июня 2011

Преобразовано в компилируемый код в 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;
    }
}

и скомпилировано, как показано в G ++ 4.6.0 на MacOS X 10.6.8, выдает указанные ошибки:

$ 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;
    }
}

С тремя дополнительными парами фигурных скобок вы больше не прыгаете в блоки, где переменные объявляются и инициализируются, поэтому код является корректным и корректно компилируется в командной строке, показанной ранее.

1 голос
/ 18 февраля 2014

Решение здесь таково: Добавьте скобки к каждому CASE

switch (i){
  case 1:{
        Tree t2 (45);
        break;
  }
  case 2: {//illegal: cannot jump past ctor 
        Tree t3 (45);
        break;
  }

}

Я не знаю, что это за сумасшествие!!!!Но Add {и} может решить эту проблему!

0 голосов
/ 13 июля 2016

Я пришел сюда, потому что у меня была такая же проблема.
Что помогло мне, так это декорировать «Дерево» за пределами case.
И не Tree t2 (45);, а разместить Tree t2; до switch.
И t2 (45); внутри case 1.

А затем вам нужно сделать то же самое для Tree t3 (45);.

0 голосов
/ 30 июня 2011

Вы не можете goto или case через конструкторы.Встроенные функции не имеют конструкторов.

Компилятор знает, когда он входит в функцию, каково общее пространство его локальных переменных, и он будет перемещать указатель стека для их размещения.Эта память неинициализирована.

Вызывает конструкторы и деструкторы во время выполнения функции, как это необходимо.Вот почему вы не можете использовать goto или case таким образом - это нарушает инварианты.Такие операторы, как break, вызывают деструкторы по мере необходимости, скажем, в цикле for, и все работает нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...