LINUX.ORG.RU

[C++][goto]«Оператор goto перескакивает через конструктор» - это правда???

 


0

0

веревка достаточной длины чтобы выстрелить себе в ногу

Этот метод не срабатывает в С++, потому что функции конструктора вызываются неявно в качестве части объявления; объявление часто скрывает вызов функции. Если вы пропускаете объявление, то вы пропускаете и вызов конструктора. Например, в следующей программе деструктор для x вызовется, а конструктор нет:

foo()
{
if ( некое_условие )
goto выход;
некий_класс x; // Конструктор не вызывается. (Оператор goto
// перескакивает через него.)
// ...
выход:
// Здесь вызывается деструктор для x при выходе x из
// области видимости.
}
Вследствие этой проблемы лучше всего совсем избегать переходов goto в программах на С++.

★★★★★

Ответ на: комментарий от legolegs

>А если у тебя, к примеру, пять вложенных for и надо из внутреннего выйти наружу?

Я отказался от goto лет 17 назад. Как только появился QBasic/QB :)

И с тех пор не возвращался к этому оператору ни разу. В смысле, не из принципа, а потому что он мне просто не нужен был. (Понятно, что есть исключения, типа DOS bat-файлов или ассемблера, но я про ЯВУ говорю).

А если тебе требуется goto из пяти вложенных циклов - то у тебя ошибка дизайна. Читай «Thinking Forth» Лео Броуди, там этот вопрос рассматривается.

KRoN73 ★★★★★
()
Ответ на: комментарий от legolegs

>Вот пример, как я считаю, оправданного применения готу:

Правильно оно решается так:

Obj found;
for (int i = 0; i < 100; i++)
    for (int j = 0; j < 100; j++)
        for (int k = 0; k < 100; k++)
            for (int l = 0; l < 100; l++)
                for (int m = 0; m < 100; m++)
                    if (found=some(i,j,k,l,m))
                    {
                        do_smth_with(found);
                        return;
                    }
...

А, вообще, я не помню в своей практие более трёх вложений циклов.

KRoN73 ★★★★★
()
Ответ на: комментарий от lester

>тогда писать на ассемблере - это "дрочить вприсядку" и быстро бегать при этом :)

Я бы перефразировал: "дрочить вприсядку, стоя на руках, чтоб потом быстро бегать, правда только в пределах своей комнаты" =)

>если goto надо - чего бы и не поставить

Ума не приложу, где бы в программе на языке высокого уровня (вроде C++, о котором речь в данном треде) нельзя было бы обойтись без goto.

Demon37 ★★★★
()
Ответ на: комментарий от Reset

а решение KRoN73? ( как я и писал - такие вложенные циклы просто вынести в отдельную функцию, заодно разгрузить код и сделать его более читабельным )

lester ★★★★
()
Ответ на: комментарий от lester

Тройной цикл -- типичная ситуация в числодробильных задачах. И если ты алгоритм на страничку будешь размазывать по функциям, то он точно станет нечитабельным да еще и неэффективным.

Reset ★★★★★
()
Ответ на: комментарий от Demon37

Почему goto непредсказуемо? Код прежде всего должен быть понятным. Если стоит goto и сразу после цикла идет label, то сразу ясно что имелось ввиду и думать не надо. Над твоим же решением придется думать.

Reset ★★★★★
()
Ответ на: комментарий от lester

>> лишних сущностей там быть не должно.

>с каких пор фунция стала сущностью

Как только стало возможным получить указатель на нее.

LamerOk ★★★★★
()
Ответ на: комментарий от KRoN73

>>лучше всего совсем избегать переходов goto в программах

лучше всего совсем избегать goto в темных переходах..

LamerOk ★★★★★
()
Ответ на: комментарий от Reset

> Тройной цикл -- типичная ситуация в числодробильных задачах. И если ты алгоритм на страничку будешь размазывать по функциям, то он точно станет нечитабельным да еще и неэффективным.

вы тоже про inline не слышали? ну и давайте не будем перекручивать - речь шла о пяти циклах, насчет "числодробильных задач" - раз это настолько типично, то можно плз пример кода для решения такой задачи( на страничку ес-но ), чтоб вы не были голословным и мы наглядно оценили надо там goto или нет

lester ★★★★
()

Бенчмарк выходов из циклов

Ниже приведён код, которых я сегодня гонял в профайлере и так.
По данным cacherind места распределились следующим образом:
loops_goto      74965434
loops_counters  74965463
loops_return    74965637
loops_throw     75066212
loops_flag      75310638

Первые три кандидата могут считаться эквивалентными по 
производительности и выбирать надо исходя из читаемости.
Поразмыслив, я пришёл к выводу, что вариант loops_return
не имеет недостатков вообще (при условии inline-подстановки
нижележащей функции). Спасибо дорогим лоровцам, подталкивавшим
меня к этой мысли :)
На втором месте оказывается goto (при условии корректного 
именования меток и общей бдительности).
Далее я поставлю уродливый и чреватый ошибками типа "забыл
одну переменную из пяти" метод с обнулением счётчиков.
С минимальным отрывом за ним идёт бабайный, небезопасный
и обременённых накладными расходами вариант loops_throw.
И наконец, в одну топку с пузырьковой сортировкой 
отправляется loops_flag.

/*******************************************************/
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

void loops_goto();
void loops_flag();
void loops_throw();
void loops_counters();
void loops_return();

class NotFoundException {};
int N;
int findme;
int main(int argc, char ** argv)
{
  struct FU
  {
    void(*func)();
    const char * capt;
  } fua[] =
  {
    {loops_throw,	"loops_throw"},
    {loops_goto,	"loops_goto"},
    {loops_flag,	"loops_flag"},
    {loops_counters,	"loops_counters"},
    {loops_return,	"loops_return"},

  };
  if (argc == 2)
    N = atoi(argv[1]);
  else
    N = 80;
  findme = 999;
  clock_t t, t2;
  for (unsigned i = 0;i < sizeof(fua) / sizeof(*fua); i++)
  {
    t = clock();
    try
    {
      fua[i].func();
    }
    catch (NotFoundException)
    {
      //cerr << "NotFoundException" << endl;
    }
    t2 = clock() - t;
    cout << fua[i].capt << ":\t" << (float)t2 / CLOCKS_PER_SEC << endl;
  }
}

//-------------------------------------
inline bool some(int i, int j, int k, int l, int m)
{
  return ( m == findme );
}
void do_smth_with(int i, int j, int k, int l, int m)
{
  cout << "\ti=" << i
  << "\tj=" << j
  << "\tk=" << k
  << "\tl=" << l
  << "\tm=" << m << endl;
}
//-------------------------------------
void loops_goto()
{
  int i, j, k, l, m;
  for (i = N; i != 0; i--)
    for (j = N; j != 0; j--)
      for (k = N; k != 0; k--)
        for (l = N; l != 0; l--)
          for (m = N; m != 0; m--)
          {
            if ( some ( i, j, k, l, m ) )
            {
              goto leave_i_loop;
            }
          }
  throw NotFoundException();
leave_i_loop:
  do_smth_with(i, j, k, l, m);
  ;
}

//-------------------------------------
void loops_counters()
{
  int i, j, k, l, m;
  int i2 = -1, j2, k2, l2, m2;
  for (i = N; i != 0; i--)
    for (j = N; j != 0; j--)
      for (k = N; k != 0; k--)
        for (l = N; l != 0; l--)
          for (m = N; m != 0; m--)
          {
            if ( some ( i, j, k, l, m ) )
            {
              i2 = i;
              j2 = j;
              k2 = k;
              l2 = l;
              m2 = m;
              i = j = k = l = m = 0;
            }
          }
  if (i2 == -1)
    throw NotFoundException();
  do_smth_with(i, j, k, l, m);
}
//-------------------------------------
void loops_flag()
{
  int i, j, k, l, m;
  bool leave_loops = false;
  for (i = N; i != 0; i--)
  {
    for (j = N; j != 0; j--)
    {
      for (k = N; k != 0; k--)
      {
        for (l = N; l != 0; l--)
        {
          for (m = N; m != 0; m--)
          {
            if ( some ( i, j, k, l, m ) )
            {
              leave_loops = true;
              break;
            }
          }
          if (leave_loops) break;
        }
        if (leave_loops) break;
      }
      if (leave_loops) break;
    }
    if (leave_loops) break;
  }
  if (!leave_loops)
    throw NotFoundException();
  do_smth_with(i, j, k, l, m);
}
//-------------------------------------
class ExitFromLoop {};

void loops_throw()
{
  int i, j, k, l, m;
  try
  {
    for (i = N; i != 0; i--)
      for (j = N; j != 0; j--)
        for (k = N; k != 0; k--)
          for (l = N; l != 0; l--)
            for (m = N; m != 0; m--)
            {
              if ( some ( i, j, k, l, m ) )
              {
                throw ExitFromLoop();
              }
            }
    throw NotFoundException();
  }
  catch (ExitFromLoop) {}
  do_smth_with(i, j, k, l, m);
}
//-------------------------------------
bool find(int*, int*, int*, int*, int*);

void loops_return()
{
  int i, j, k, l, m;
  if (!find(&i, &j, &k, &l, &m))
    throw NotFoundException();
  do_smth_with(i, j, k, l, m);
}

bool find(int *pi, int *pj, int *pk, int *pl, int *pm)
{
  int i, j, k, l, m;
  for (i = N; i != 0; i--)
    for (j = N; j != 0; j--)
      for (k = N; k != 0; k--)
        for (l = N; l != 0; l--)
          for (m = N; m != 0; m--)
            if ( some ( i, j, k, l, m ) )
            {
              *pi = i;
              *pj = j;
              *pk = k;
              *pl = l;
              *pm = m;
              return true;
            }
  return false;
}

//-------------------------------------

legolegs ★★★★★
() автор топика
Ответ на: комментарий от Reset

>Почему goto непредсказуемо?

http://www.linux.org.ru/view-message.jsp?msgid=3520569

>Код прежде всего должен быть понятным.

Прежде всего должен быть правильно работающим. Да и о какой читабельности может идти речь, если, например, какой-нибудь программист использовал где-нибудь в конце многостраничного листинга, а label поставил в едва ли не в самом начале?

Demon37 ★★★★
()
Ответ на: комментарий от Reset

> при читабельность кода слышали ?

смешно - чем вам inline не угодил?

lester ★★★★
()
Ответ на: комментарий от KRoN73

>Понятно, что есть исключения, типа DOS bat-файлов или ассемблера, но я про ЯВУ говорю)

В яве есть специальный break для выхода из нескольких циклов, емнип. В С и C++ нет и не будет.

legolegs ★★★★★
() автор топика
Ответ на: комментарий от Demon37

>использовал где-нибудь

использовал goto где-нибудь

fxd

Demon37 ★★★★
()
Ответ на: комментарий от Demon37

> http://www.linux.org.ru/view-message.jsp?msgid=3520569

Этот код не компилируется gcc

> Да и о какой читабельности может идти речь, если, например, какой-нибудь программист использовал где-нибудь в конце многостраничного листинга, а label поставил в едва ли не в самом начале?

Это не аргумент, потому что сдуру можно и %уй сломать.

Reset ★★★★★
()
Ответ на: комментарий от legolegs

>В яве есть специальный break для выхода из нескольких циклов, емнип

Возможно. Но надобности не было, так что я про него и не слышал :)

...

Всё же, всем сторонниками GOTO рекомендую почитать Лео Броуди. Хорошо прочищает мозги и позволяет отучиться писать этажерки кода, в которых через полгода ногу сломишь, даже если ты сам чёрт в программировании.

А за счёт декомпозиции и рефакторинга в итоге можно высвободить массу ресурсов мозга, которые можно потратить на форумы, пьянки или поиск более эффективных алгоритмов :)

KRoN73 ★★★★★
()
Ответ на: комментарий от lester

>а почему int *pi,..., а не int& pi

Чтаемость. По вызову сразу видно, что аргументы могут меняться в функции.

>"i--" лучше заменить на "--i"

По мне, так достаточно уже замены ++ на -- - 25% прирост производительности. А Почему --i? Можно ссылку, а то я пропустил.

legolegs ★★★★★
() автор топика
Ответ на: комментарий от lester

Например такое. 
Я не спорю, что тут можно сделать без goto. 
Но с goto код выглядит прозрачным.
Насчет рефакторинга чушь абсолютная. 
Приведенный код является окончательным математически доказанным алгоритмом и никаким изменениям не подлежит.


инициализация
.......

	for (j = 0; j < k; ++j) {
		double nr1, nr2;

		Ax(ax, A, &q[j * n], n);
		nr1 = norm2(ax, n);

		for (i = 0; i <= j; ++i) {
			h[i * hz + j] = scalar2(&q[i * n], ax, n); //-> j x j
			//ax = ax - h[i * hz + j] * q[i * n];
			vector_sum2(ax, ax, &q[i * n], -h[i * hz + j], n);
		}
		
		// rotate
		for (i = 0; i <= j - 1; ++i) {
			double x = h[i * hz + j];
			double y = h[(i + 1) * hz + j];

			h[i * hz + j]       = x * c[i + 1] + y * s[i + 1];
			h[(i + 1) * hz + j] = x * s[i + 1] - y * c[i + 1];
		}

		beta = sqrt(h[j * hz + j] * h[j * hz + j] + h[(j + 1) * hz + j] * h[(j + 1) * hz + j]);
		s[j + 1]      = h[(j + 1) * hz + j] / beta;
		c[j + 1]      = h[j * hz + j] / beta;
		h[j * hz + j] = beta;

		gamma[j + 1] = s[j + 1] * gamma[j];
		gamma[j]     = c[j + 1] * gamma[j];

		if (gamma[j + 1]  > eps) {
			vector_mult_scalar(&q[(j + 1) * n], ax, 1.0 / h[(j + 1) * hz + j], n);
		} else {
			goto done;
		}
	}

	--j;

done:
	ret = gamma[j + 1];

	{
		double * y = malloc(hz * sizeof(double));
		for (i = j; i >= 0; --i) {
			double sum = 0.0;
			for (k = i + 1; k <= j; ++k) {
				sum += h[i * hz + k] * y[k];
			}
			y[i] = (gamma[i] - sum) / h[i * hz + i];
		}

		for (i = 0; i <= j; ++i) {
			vector_sum2(x, x, &q[i * n], y[i], n);
		}
		free(y);
	}

end:
	free(q);  free(r);
	free(ax); free(h);
	free(gamma);
	free(s); free(c);

	return ret;

Reset ★★★★★
()
Ответ на: комментарий от Reset

а разве там break вместо goto не катит?

П.С. имена переменных из разряда - угадай сам, про комментарии вообще молчу

lester ★★★★
()
Ответ на: комментарий от Reset

>Приведенный код является окончательным математически доказанным алгоритмом и никаким изменениям не подлежит.

Ох уж эти математики. Они как-то так мыслят, что придуманное ими можно реализовать либо после долгого обдумывания, либо сразу, но с goto.

legolegs ★★★★★
() автор топика
Ответ на: комментарий от lester

> а разве там break вместо goto не катит?

В таком виде как тут написано не катит.

> П.С. имена переменных из разряда - угадай сам

Имена переменные общепринятые математические. Там выше по коду есть ссылка на статью, кому надо посмотрит и сразу всё поймет.

Если писать в java-style. superPuperMatricaKotoruuVrashem[superPuperSchetchik] = cosinus[superPuperSchetchik] ....

то тогда в этом нельня будет разобраться и со статьей.

> , про комментарии вообще молчу

Коментарии, описывающие что делаем. Специалист поймет. А пыхпых кодер разумеется нет.

Reset ★★★★★
()
Ответ на: комментарий от Reset

>Этот код не компилируется gcc

Не проверял, верю на слово. Тем более, значит goto в таких ситуациях не применим.

>Это не аргумент, потому что сдуру можно и %уй сломать.

Если какое-то средство языка позволяет устроить бардак, будь уверен, он будет устроен.

Demon37 ★★★★
()
Ответ на: комментарий от kub

Можно, но с освобождением ресурсов все равно что-то делать придется.

Reset ★★★★★
()
Ответ на: комментарий от Demon37

В Си и тем более в Си++ любое средство языка позволяет устроить бардак, давай вообще не будем на них писать.

Reset ★★★★★
()
Ответ на: комментарий от Reset

> В таком виде как тут написано не катит.

вы хотите сказать, что замена "goto done;" на "break" - что-то изменит в работе кода?

> Имена переменные общепринятые математические. Там выше по коду есть ссылка на статью, кому надо посмотрит и сразу всё поймет.

Если писать в java-style. superPuperMatricaKotoruuVrashem[superPuperSchetchik] = cosinus[superPuperSchetchik] ....
то тогда в этом нельня будет разобраться и со статьей.

вы опять кидаетесь в крайности

> Коментарии, описывающие что делаем. Специалист поймет. А пыхпых кодер разумеется нет.


из комментариев там только "// rotate", насчет - "Специалист поймет. А пыхпых кодер разумеется нет", поймет и тот и тот, главное сколько времени уйдет на понимание кода, чтоб его грамотно подправить

lester ★★★★
()
Ответ на: комментарий от legolegs

>А Почему --i?

Проверил так:

#include <stdio.h> 

int main()
{
    int count, s, res;
    res = scanf("%d", &count);

    s = 0;
    for(int i=count-1; i>=0; --i)
        s+=i*i;
    printf("%d ", s);

    s = 0;
    for(int i=count-1; i>=0; i--)
        s+=i*i;
    printf("%d ", s);

    s = 0;
    for(int i=0; i<count; i++)
        s+=i*i;
    printf("%d ", s);

    s = 0;
    for(int i=0; i<count; ++i)
        s+=i*i;
    printf("%d ", s);

    return 0;
}

В результате интересно. В случае --i на одно сравнение больше:

--i:
    cmpl    $5, %esi
    jbe .L27
    testl   %eax, %eax
    jne .L4
.L27:
    xorl    %edx, %edx

i--:
    cmpl    $5, %esi
    ja  .L46
.L28:
    xorl    %edx, %edx

С «++» результат не так предсказуем, растактовку посчитать на
нынешних процессорах не могу, попробую отбенчить сейчас :)

KRoN73 ★★★★★
()
Ответ на: комментарий от lester

> вы хотите сказать, что замена "goto done;" на "break" - что-то изменит в работе кода?

Нет. Там разные значения j ожидаем после цикла.

> "// rotate", насчет - "Специалист поймет. А пыхпых кодер разумеется нет", поймет и тот и тот, главное сколько времени уйдет на понимание кода, чтоб его грамотно подправить

Даже если в комментах там расписать в чем суть метода вращения, то на быстроту понимания для пыхпых кодера это не скажется.

Reset ★★★★★
()
Ответ на: комментарий от Reset

>В Си и тем более в Си++ любое средство языка позволяет устроить бардак, давай вообще не будем на них писать.

_Такой_ жуткий бардак, какой можно с помощью goto устроить, ни одно другое средство не позволит сделать. Мне столько жутких исходников, напичканных goto, пришлось перелопатить, что я уже давно сам избегаю использования goto без крайней нужды и другим советую избегать.

Demon37 ★★★★
()
Ответ на: комментарий от Demon37

>Мне столько жутких исходников, напичканных goto, пришлось перелопатить
+1
Особенно страшно, когда goto назад

kub
()

опять опечатка - забыл }

П.С. когда уже на ЛОР прикрутят редактирование сообщений :)

lester ★★★★
()
Ответ на: комментарий от lester

Впринципе пойдет, если везде ниже заменить j+1 на j.

Остается освобождение ресурсов в случае неудачной инициализации.

Reset ★★★★★
()
Ответ на: комментарий от KRoN73

>попробую отбенчить сейчас :)

5000000 циклов с суммированием квадратов от 0 до 1000. Prescott 2.8ГГц.

По минимальным результатам выходит для --i в итоге 6.14 секунды, для всех остальных - 6.10 секунды :)

Даже с nice -19 разброс определённый есть, но --i всегда медленнее остальных.

...

А, у меня есть удалённая простаивающая машина. Сейчас на ней протестирую...

KRoN73 ★★★★★
()
Ответ на: комментарий от Reset

> Остается освобождение ресурсов в случае неудачной инициализации.

надо смотреть код выше - возможно и нет смысла выделять ресурсы( сразу сделать проверку ), тогда goto заменится на return

lester ★★★★
()
Ответ на: комментарий от Reset

>Ох уж эти явисты, чую после этого "рефакторинга" в коде разобраться будет нельзя :)

Нет, Фортеры. Только в то время это называлось декомпозицией кода :) Но рефакторинг короче и сегодня понятнее.

KRoN73 ★★★★★
()
Ответ на: комментарий от lester

В двух местах освобождение ресурсов будет если убирать goto. 
Тут еще хороший случай, так как проверка всего лишь одна, 
а когда их десяток.


	double * r  = malloc(n * sizeof(double)); /* b - Ax */
	double * ax = malloc(n * sizeof(double));

....

	Ax(ax, A, x, n);
	vector_diff(r, b, ax, n);

	gamma_0 = norm2(r, n);

	if (gamma_0 < eps) {
		ret = gamma_0;
		goto end;
	}

....

Reset ★★★★★
()
Ответ на: комментарий от KRoN73

>Сейчас на ней протестирую...

Круто. На той машине всё совсем иначе:

--i и i-- равно одинаково, 6.16сек.
++i и i++ равно одинаково, 7.56сек.

Кто бы мог подумать, что на разных процессорах (в первом случае P4, во втором C2D) такая разница будет...

KRoN73 ★★★★★
()
Ответ на: комментарий от lester

> ужас :) тут надо либо рефакторинг

Про "рефакторинг" я уже устал сказки слушать. Давай конкретный пример.

> либо smart_ptr

нафиг нафиг сюда в чистый Си тянуть плюсы из-за того, что у кого-то есть религиозные претензии к goto.

Reset ★★★★★
()

Эх, молодежь...
Не нюхала старого фортрановского кода на волшебных клубочках из GoTo...
Это же невоиспроизводимо-задокументированый ход мысли озабоченного-романтического ботаника с интригующей, по тем временам, профессией - ПРОГРАММИСТ, или, того круче СИСТЕМНЫЙ АДМИНИСТРАТОР.

Романтика, хули...

ovax ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.