Форум программистов, компьютерный форум CyberForum.ru

Coding style или нет - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 23, средняя оценка - 4.65
HighPredator
 Аватар для HighPredator
5349 / 1732 / 320
Регистрация: 10.12.2010
Сообщений: 5,119
Записей в блоге: 3
09.02.2012, 19:56     Coding style или нет #1
Услышал сегодня от коллеги такую интересную вещь: есть блоки кода ограниченные командными скобками {}. Так вот, рекомендуется переменные, используемые в блоках и только в них, объявлять в таких блоках. Я например, как правило объявляю переменные в начале подпрограмм. Привычка. Вопрос такой: это чисто coding style рекомендация или есть какое-то практическое значение подобного действия?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
HighPredator
 Аватар для HighPredator
5349 / 1732 / 320
Регистрация: 10.12.2010
Сообщений: 5,119
Записей в блоге: 3
09.02.2012, 22:32  [ТС]     Coding style или нет #21
Осознал. Всем спасибо!
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Байт
 Аватар для Байт
13974 / 8805 / 1227
Регистрация: 24.12.2010
Сообщений: 15,949
09.02.2012, 23:07     Coding style или нет #22
Цитата Сообщение от AzaKendler Посмотреть сообщение
какое то время они лежат нетронутыми пока в стеке есть место.
Они лежат нетронутыми до вызова другой функции или входа в другой блок.
Представь себе стек, как стопку книг. Из блока (функции) выходим - книжка (автоматические переменные) снимается. Входим в другую - на стопку кладется ее брошюрка.
Bers
Заблокирован
10.02.2012, 08:00     Coding style или нет #23
Цитата Сообщение от Сtrl Посмотреть сообщение
C++
1
2
3
4
5
// плохой код
int n, i, sum;
sum = 0;
for (i = 0, n = 10; i < n; i++)
  sum += i;
А такой?
C++
1
2
3
4
5
6
7
int sum=0; const size_t n=10;
for (size_t i = 0; i < n; i++)
{
     CBar bar;  //предположим, дорогое создание
                   //за пределами цикла не используется.
     sum +=  bar.Work(); 
}
А такой?
C++
1
2
3
4
int sum=0; const size_t n=10;
CBar bar;  //предположим, дорогое создание
              //за пределами цикла не используется.
for (size_t i = 0; i < n; i++)   sum +=  bar.Work();
А такой?
C++
1
2
3
4
5
6
7
int sum=0; 
{
    const size_t n=10;
    CBar bar;  //предположим, дорогое создание
                 //за пределами цикла не используется.
    for (size_t i = 0; i < n; i++)   sum +=  bar.Work(); 
}
Лично я использую вот такую каноническую форму:
C++
1
2
3
4
5
6
7
8
void Foo()
{
    //Зона объявлений всех участников алгоритма
 
    //Зона боевых действий
 
    //Зона чистки
}
При этом действует правило "близкого объявления". То бишь, где объявил, там и используй.
Однако, все объявления - только в зоне объявлений участников.

Получается, если запрещаю себе создавать сущности непосредственно по месту использования в боевом коде (за искл. индексов циклов for, значения которых не используется после окончания цикла), то как же мне суметь выдержать правило "близкого объявления" ?

Очень просто - не раздувать тело функции. Если используемая переменная оказывается слишком далеко от места объявления, значит функция "слишком раздутая". Это красноречивый признак говнокодистости.

Значит стоит подумать о том, что алгоритм слишком тяжелый, и возможно он решает не одну задачу, а множество задач. И стоит прочистить его структуру - разбить большую сложную задачу на кучку мелких.

Весь мой код внешне состоит из 100500 функций, и все - мелкие. Редко когда туловище функции-члена не влазит целиком на страничку моего экрана. Поэтому всегда можно окинуть мысленным взором функцию целиком. Сразу понять, что она делает. Если нужно при этом ещё понять, как она это делает - можно глянуть какие функции она дергает, что бы увидеть их непосредственную реализацию.
Получается, что код читается по принципу: сначала "что делает функция?", затем "как она это делает?"
Такой код не сложно читать.

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

В результате получаются функции-тяжеловесы из 100500 строк, которые уже не отвечает на вопросы "что они делают". А сразу отвечают на вопросы "как они делают нечто".

Не зная "что", ответ на вопрос "как" становится уже не очевиден. Такой код труднее понять.
А учитывая, что такие "раздутые" алгоритмы решают сразу несколько задач - труднее вдвойне.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16827 / 5248 / 321
Регистрация: 30.03.2009
Сообщений: 14,129
Записей в блоге: 26
10.02.2012, 09:43     Coding style или нет #24
Цитата Сообщение от Bers Посмотреть сообщение
При этом действует правило "близкого объявления". То бишь, где объявил, там и используй.
Однако, все объявления - только в зоне объявлений участников
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void func (void)
{
  std::string str = "abc";
  str += "efg";
  func2 (str);
 
  // Далее идёт большое количество кода, которое уже не использует переменную str
  // Если бы мы верхние три строки облачили в фигурные скобки, что деструктор str
  // вызвался бы сразу и освободил лишнюю память, занятую строкой. Но теперь
  // деструктор вызовется только в конце процедуры, а потребляемая string'ом память
  // всё это время будет висеть мёртвым грузом. Это одно из тех удобств Си++, которое
  // мне не нравится из-за того, что у программиста нет удобных средств для контролирования
  // за этим процессом
  ...
 
}
Цитата Сообщение от Bers Посмотреть сообщение
Если используемая переменная оказывается слишком далеко от места объявления, значит функция "слишком раздутая". Это красноречивый признак говнокодистости
Цитата Сообщение от Bers Посмотреть сообщение
И стоит прочистить его структуру - разбить большую сложную задачу на кучку мелких
Абсолютный бред, основанный на том, что ты пока не имел опыта в написании большого и сложного софта
Bers
Заблокирован
10.02.2012, 10:09     Coding style или нет #25
Evg
C++
1
2
3
4
5
6
7
8
void func (void) {  std::string str = "abc"; str += "efg";  func2 (str); }
 
void bar(void)
{
  // Далее идёт большое количество кода, которое уже не использует переменную str
  // И даже не подозревает о её существовании
  ...
}
Вообще, формулировка "большое количество кода" намекает, что "код делает очень многое".
Получаем функцию, которая выполняет не одну конкретную задачу, а множество разных задач.
Выглядит это все как монолитная размазня на 100500 строк.

Цитата Сообщение от Evg Посмотреть сообщение
Абсолютный бред, основанный на том, что ты пока не имел опыта в написании большого и сложного софта
Просто мир не совершенен. А в несовершенном мире говнокод оказывается жизнеспособнее красивого кода. Потому что тупо некогда заниматься красивостями. Потому что порой он слишком часто меняется, и нет смысла тратить много времени на то, что с высокой степенью вероятности нужно будет править.

В реальном мире масштабируемая архитектура на весь золота. Потому что позволяет нивелировать потенциальный вред от говнокода за счет продуманной техники его инкапсуляции.

Однако и в реальном мире есть показательные примеры. Допустим библиотека LOKI и STL как небо и земля.
В LOKI сможет разобраться даже тот программист, который имеет только базовые знания о шаблонах, даже не читая документации.
В STL без 100 грамм реально не разберёшься.

http://cs10041.vk.com/u226973/144727247/y_1385123a.jpg

А что до меня - я много раз наблюдал за собой такой эффект, когда трудно становится удержать в голове алгоритм выполнения задачи. И чем дальше в лес - тем сложнее. Для меня это сигнал, что нужно остановится, и подумать, как сложную задачу раздробить на кучку мелких.
"Разделяй и властвуй".
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 11:58     Coding style или нет #26
Цитата Сообщение от Evg Посмотреть сообщение
Если бы мы верхние три строки облачили в фигурные скобки,
спасибо. не обращал внимание. это обрезает область видимости оказывается. просто скобки.
интересный момент


Цитата Сообщение от Evg Посмотреть сообщение
. Но теперь
// деструктор вызовется только в конце процедуры, а потребляемая string'ом память
// всё это время будет висеть мёртвым грузом. Это одно из тех удобств Си++, которое
// мне не нравится из-за того, что у программиста нет удобных средств для контролирования
// за этим процессом
C++
1
str.~basic_string();
не подойдет для такого управления?
soon
 Аватар для soon
2536 / 1301 / 81
Регистрация: 09.05.2011
Сообщений: 3,086
Записей в блоге: 1
10.02.2012, 12:18     Coding style или нет #27
Цитата Сообщение от AzaKendler Посмотреть сообщение
не подойдет для такого управления?
Не надо деструкторы где попало вызывать. Даже не так. Вообще не надо деструкторы вызывать.
code
Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
soon@desktop:~/Src/C++/main$ cat main.cpp
#include <string>
 
int main()
{
    std::string str("asd");
    str.~basic_string();
    return 0;
}
soon@desktop:~/Src/C++/main$ g++ main.cpp -o main && ./main
*** glibc detected *** ./main: double free or corruption (fasttop): 0x09d04008 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6ebc2)[0x899bc2]
/lib/i386-linux-gnu/libc.so.6(+0x6f862)[0x89a862]
/lib/i386-linux-gnu/libc.so.6(cfree+0x6d)[0x89d94d]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0xe8180f]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSs4_Rep10_M_destroyERKSaIcE+0x1b)[0xe68c4b]
/usr/lib/i386-linux-gnu/libstdc++.so.6(+0x94c8c)[0xe68c8c]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSsD1Ev+0x2e)[0xe68cfe]
./main[0x80486a7]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x844113]
./main[0x8048581]
======= Memory map: ========
001ef000-001f0000 r-xp 00000000 00:00 0          [vdso]
00411000-00439000 r-xp 00000000 08:01 5899205    /lib/i386-linux-gnu/libm-2.13.so
00439000-0043a000 r--p 00028000 08:01 5899205    /lib/i386-linux-gnu/libm-2.13.so
0043a000-0043b000 rw-p 00029000 08:01 5899205    /lib/i386-linux-gnu/libm-2.13.so
0082b000-009a1000 r-xp 00000000 08:01 5899175    /lib/i386-linux-gnu/libc-2.13.so
009a1000-009a3000 r--p 00176000 08:01 5899175    /lib/i386-linux-gnu/libc-2.13.so
009a3000-009a4000 rw-p 00178000 08:01 5899175    /lib/i386-linux-gnu/libc-2.13.so
009a4000-009a7000 rw-p 00000000 00:00 0 
00afb000-00b19000 r-xp 00000000 08:01 5899162    /lib/i386-linux-gnu/ld-2.13.so
00b19000-00b1a000 r--p 0001d000 08:01 5899162    /lib/i386-linux-gnu/ld-2.13.so
00b1a000-00b1b000 rw-p 0001e000 08:01 5899162    /lib/i386-linux-gnu/ld-2.13.so
00d52000-00d6e000 r-xp 00000000 08:01 5899196    /lib/i386-linux-gnu/libgcc_s.so.1
00d6e000-00d6f000 r--p 0001b000 08:01 5899196    /lib/i386-linux-gnu/libgcc_s.so.1
00d6f000-00d70000 rw-p 0001c000 08:01 5899196    /lib/i386-linux-gnu/libgcc_s.so.1
00dd4000-00eb2000 r-xp 00000000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00eb2000-00eb3000 ---p 000de000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00eb3000-00eb7000 r--p 000de000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00eb7000-00eb8000 rw-p 000e2000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00eb8000-00ebf000 rw-p 00000000 00:00 0 
08048000-08049000 r-xp 00000000 08:01 797557     /home/soon/Src/C++/main/main
08049000-0804a000 rw-p 00000000 08:01 797557     /home/soon/Src/C++/main/main
09d04000-09d25000 rw-p 00000000 00:00 0          [heap]
b7700000-b7721000 rw-p 00000000 00:00 0 
b7721000-b7800000 ---p 00000000 00:00 0 
b78be000-b78c1000 rw-p 00000000 00:00 0 
b78d2000-b78d4000 rw-p 00000000 00:00 0 
bfa57000-bfa78000 rw-p 00000000 00:00 0          [stack]
Aborted
soon@desktop:~/Src/C++/main$ cat main1.cpp
#include <string>
 
int main()
{
    std::string str("asd");
    return 0;
}
soon@desktop:~/Src/C++/main$ g++ main1.cpp -o main && ./main
soon@desktop:~/Src/C++/main$
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 12:58     Coding style или нет #28
как видишь чисто. и никаких ругательств при сборке не было

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

да и сам ты если поместишь объект в какую то "свою" область то потом обязан будешь вызвать деструктор самостоятельно а не просто уничтожить область.


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Test
{
    Test():a(40){};
    ~Test(){};
    int a;
};
 
int main()
{
 
    char* b = new char [100];
    Test* s = new(b) Test;
 
    s->a =7 ;
    s->~Test();
    delete [] b;
    return 0;
}
Миниатюры
Coding style или нет  
soon
 Аватар для soon
2536 / 1301 / 81
Регистрация: 09.05.2011
Сообщений: 3,086
Записей в блоге: 1
10.02.2012, 13:06     Coding style или нет #29
Цитата Сообщение от AzaKendler Посмотреть сообщение
и никаких ругательств при сборке не было
При сборке и не будет.

Цитата Сообщение от AzaKendler Посмотреть сообщение
да и сам ты если поместишь объект в какую то "свою" область то потом обязан будешь вызвать деструктор самостоятельно а не просто уничтожить область.
Пример можете привести?
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 13:11     Coding style или нет #30
Цитата Сообщение от soon Посмотреть сообщение
Пример можете привести
привел выше. он конечно оторван от реальности. но при разборе темы с контейнерами и в частности с аллокаторами - эта тема с вызовом деструктора часто всплывает. более того в опрераторе delete предусмотрен все тот же явный вызов деструктора
soon
 Аватар для soon
2536 / 1301 / 81
Регистрация: 09.05.2011
Сообщений: 3,086
Записей в блоге: 1
10.02.2012, 13:24     Coding style или нет #31
Цитата Сообщение от AzaKendler Посмотреть сообщение
привел выше.
А от s память освобождать не надо?
К тому же он не отображает сути проблемы, возникшей при вызове ~basic_string().
code
Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
soon@desktop:~/Src/C++/main$ cat main1.cpp
class Foo
{
    int* _ptr;
 
public:
    Foo(): _ptr(new int(3))
    {
 
    }
 
    ~Foo()
    {
        delete _ptr;
    }
};
 
int main()
{
    Foo f;
    f.~Foo();
    return 0;
}
soon@desktop:~/Src/C++/main$ g++ main1.cpp -o main && ./main
*** glibc detected *** ./main: double free or corruption (fasttop): 0x09d64008 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6ebc2)[0xd24bc2]
/lib/i386-linux-gnu/libc.so.6(+0x6f862)[0xd25862]
/lib/i386-linux-gnu/libc.so.6(cfree+0x6d)[0xd2894d]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0xbbc80f]
./main[0x8048613]
./main[0x80485cf]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xccf113]
./main[0x80484d1]
======= Memory map: ========
00461000-0047f000 r-xp 00000000 08:01 5899162    /lib/i386-linux-gnu/ld-2.13.so
0047f000-00480000 r--p 0001d000 08:01 5899162    /lib/i386-linux-gnu/ld-2.13.so
00480000-00481000 rw-p 0001e000 08:01 5899162    /lib/i386-linux-gnu/ld-2.13.so
00773000-0078f000 r-xp 00000000 08:01 5899196    /lib/i386-linux-gnu/libgcc_s.so.1
0078f000-00790000 r--p 0001b000 08:01 5899196    /lib/i386-linux-gnu/libgcc_s.so.1
00790000-00791000 rw-p 0001c000 08:01 5899196    /lib/i386-linux-gnu/libgcc_s.so.1
009ae000-009d6000 r-xp 00000000 08:01 5899205    /lib/i386-linux-gnu/libm-2.13.so
009d6000-009d7000 r--p 00028000 08:01 5899205    /lib/i386-linux-gnu/libm-2.13.so
009d7000-009d8000 rw-p 00029000 08:01 5899205    /lib/i386-linux-gnu/libm-2.13.so
00b0f000-00bed000 r-xp 00000000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00bed000-00bee000 ---p 000de000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00bee000-00bf2000 r--p 000de000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00bf2000-00bf3000 rw-p 000e2000 08:01 3806517    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
00bf3000-00bfa000 rw-p 00000000 00:00 0 
00cb6000-00e2c000 r-xp 00000000 08:01 5899175    /lib/i386-linux-gnu/libc-2.13.so
00e2c000-00e2e000 r--p 00176000 08:01 5899175    /lib/i386-linux-gnu/libc-2.13.so
00e2e000-00e2f000 rw-p 00178000 08:01 5899175    /lib/i386-linux-gnu/libc-2.13.so
00e2f000-00e32000 rw-p 00000000 00:00 0 
00f5d000-00f5e000 r-xp 00000000 00:00 0          [vdso]
08048000-08049000 r-xp 00000000 08:01 797557     /home/soon/Src/C++/main/main
08049000-0804a000 rw-p 00000000 08:01 797557     /home/soon/Src/C++/main/main
09d64000-09d85000 rw-p 00000000 00:00 0          [heap]
b7600000-b7621000 rw-p 00000000 00:00 0 
b7621000-b7700000 ---p 00000000 00:00 0 
b779f000-b77a2000 rw-p 00000000 00:00 0 
b77b3000-b77b5000 rw-p 00000000 00:00 0 
bf8b6000-bf8d7000 rw-p 00000000 00:00 0          [stack]
Aborted
soon@desktop:~/Src/C++/main$
Bers
Заблокирован
10.02.2012, 13:30     Coding style или нет #32
Цитата Сообщение от soon Посмотреть сообщение
К тому же он не отображает сути проблемы, возникшей при вызове ~basic_string().
В приведенном коде, проблема не в том, что был явно вызван диструктор. А в том, что он в принципе был неправильно записан. Указатель не был переведён в "не рабочее" состояние.
Если бы программист после удаления, занулил бы его, то повторный вызов диструктора не привел бы к трагедии.

Что до самих явно вызываемых диструкторов, имхо - это плохая идея, если конечно, вы не пишите какой нибудь пул памяти с new placement
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 13:35     Coding style или нет #33
Цитата Сообщение от soon Посмотреть сообщение
А от s память освобождать не надо?
не надо - это синтаксис размещения.
и в данном случае s->~Test() служит для очистки, на тот случай если в Test() были переменные указывающие на динамически созданные объекты.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Test
{
        Test():a(40){};
        ~Test(){};
        int a;
};
 
 
int main()
{ 
        char* b = new char [100];
        Test* s = new(b) Test; 
      
        //s->~Test();
        delete [] b;// в этом пример оператор "зачистит" и члены тест.
        return 0;
}

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Test
{
        Test():a(new int(40)){};
        ~Test(){delete a; a=0;};
        int* a;
};
 
int main()
{ 
        char* b = new char [100];
        Test* s = new(b) Test; 
      
        s->~Test();
        delete [] b;// в этом пример оператор не сможет сам провести очистку вызов деструктора обязателен
        return 0;
}
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16827 / 5248 / 321
Регистрация: 30.03.2009
Сообщений: 14,129
Записей в блоге: 26
10.02.2012, 13:46     Coding style или нет #34
Цитата Сообщение от Bers Посмотреть сообщение
Вообще, формулировка "большое количество кода" намекает, что "код делает очень многое"
Я не совсем правильно выразился. У тебя там может быть и три строки кода, которые в цикле из миллиона итераций вызывают другую функцию, в которой вызываются ещё функции и т.п. Но эти три строки кода будут работать долго. И всё это время память для уже ненужного объекта будет висеть занятой. Только потому, что это как бы красиво выглядит в исходнике

Цитата Сообщение от Bers Посмотреть сообщение
А в несовершенном мире говнокод оказывается жизнеспособнее красивого кода. Потому что тупо некогда заниматься красивостями
Как раз-таки нет. Дофига ситуаций, когда распиливание одной процедуры на более мелкие приводит к гавнокоду. Поэтому нельзя к программированию подходить со всякими догмами типа "функция ДОЛЖНА быть короткой", "НЕЛЬЗЯ использовать goto" и т.д. В каждом конкретном случае надо принимать конкретное решение, являющееся оптимальным в данном конкретном случае

Цитата Сообщение от AzaKendler Посмотреть сообщение
спасибо. не обращал внимание. это обрезает область видимости оказывается. просто скобки.
интересный момент
Не просто область видимости, но ещё и время жизни автоматического объекта

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool x;
 
// Допустим, так сложилось, что во время работы нашей программы "x" всегда
// равно "false" (пользователь не пытался использоватьнекоторые фичи программы)
x = false;
...
 
if (x)
{
  // В этот блок мы никогда не попадём, а следовательно, объект никогда не будет
  // создан (т.е. никогда для него не вызовутся конструктор и деструктор). Это справедливо
  // как для автоматического объекта, так и для статического, который помещён вовнутрь
  // лексического блока. Т.е. сэкономили память и время исполнения
  std::string str;
  ...
}
Цитата Сообщение от AzaKendler Посмотреть сообщение
не подойдет для такого управления?
Не подойдёт, потому что компилятор по достижении закрывающей фигурной скобки автоматически влепит вызов деструктора (и на это ты повлиять никак не можешь). Таким образом, у тебя будет два вызова деструктора. Деструктор ручками можно вызывать ТОЛЬКО в том случае, если динамический объект создавался при помощи конструкции "placement new"
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 13:48     Coding style или нет #35
soon, конкретно по твоей ошибке тебе написали уже


C++
1
2
3
4
5
~Foo()
    {
        delete _ptr;
        _ptr = 0; // обнуляем и второй вызов пройдет безболезненно
    }
пробуй

Добавлено через 2 минуты
Цитата Сообщение от Evg Посмотреть сообщение
Деструктор ручками можно вызывать ТОЛЬКО в том случае
ну вот мы вызываем же в примерах.

и если кровь из носа надо очистить память на которую ссылаются члены класса то вызов деструктора в том месте сделает это. а то что он вызовется второй раз в холостую это конечно не очень хорошо, но краша не будет если в самом деструкторе все ровно, наверно это и имелось ввиду под неудобством?
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
10.02.2012, 13:56     Coding style или нет #36
AzaKendler, деструктор не предназначен для того, чтобы вызывать его вручную. Соответственно обнулять указатель в нём просто так никто не будет, а из этого и следует падение программы, когда пытаемся к указателю на память, которая уже не принадлежит программе, применить delete.
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 13:58     Coding style или нет #37
Цитата Сообщение от silent_1991 Посмотреть сообщение
деструктор не предназначен для того, чтобы вызывать его вручную. Соответственно обнулять указатель в нём просто так никто не будет
что то ты меня удивил. как это обнулять никто не будет - этож золотое правило для экспертов с++.
почему не предназначен, можешь растолковать?
он постоянно вызывается вручную в stl.
только мы как пользователи, например vector, это не видим, если не захотим пройтись в дебаге по всему что там происходит, ну или почитать про это
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
10.02.2012, 14:00     Coding style или нет #38
AzaKendler, он вызывается вручную в стл в аллокаторае. А вам уже не раз написали, что аллокатор для создания объекта использует placement new, поэтому проблемы с двойным вызовом конструктора там нет.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16827 / 5248 / 321
Регистрация: 30.03.2009
Сообщений: 14,129
Записей в блоге: 26
10.02.2012, 14:00     Coding style или нет #39
Цитата Сообщение от AzaKendler Посмотреть сообщение
и если кровь из носа надо очистить память на которую ссылаются члены класса то вызов деструктора в том месте сделает это
Если вопрос стоит как кровь из носа, то вместо использования объекта использовать динамическое выделение памяти. И искусственно в это место засовывать placement new вовсе не к чему. Говоря про удобство и неудобство я имел в виду работу именно с ПЕРЕМЕННЫМИ типа класса, для которых компилятор АВТОМАТИЧЕСКИ вызывает конструкторы и деструкторы. Т.е. у тебя нет никакой возможности забыть вызвать деструктор

А проблема в общем случае не в том, чтобы освободить память, занимаемую экземпляром класса (как ты приводил в сових примерах). Экземпляр класса std::string занимает совсем немного места. Проблема в том, что экземпляр класса в момент создания внутри себя заводит динамически выделяемую память, которую можно освободить только удалив экземпляр класса.

Т.е. на Си я могу написать код:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct String str;
...
 
/* Как бы вызов конструктора, который я могу вставлять там, где мне заблагорассудится */
InitString (&str);
 
/* Поработали с экземпляром */
 
/* Как бы вызов деструктора, который я также могу вставлять там, где мне заблагорассудится */
ClearString (&str);
 
/* И могу даже повторно вызвать как бы конструктор (ибо захотелось мне повторно
 * проинициализировать существующий объект) */
InitString (&str);
На Си++ это выразить никак нельзя. Потому что конструктор может вызываться только автоматически и только при создании переменной. Аналогично деструктор. Единственное, что можно сделать в Си++ - так это вместо объектов использовать указатели и рожать-удалять объекты через new/delete, что, вобщем-то, совсем неэффективно. Понятно, что ровно такой код с вызовами функций можно написать и на Си++, но это уже как бы не есть программирование на Си++
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
10.02.2012, 14:07     Coding style или нет
Еще ссылки по теме:

Полиндром или нет? C++
C++ Вывести на экран слова, в которых все символы повторяющиеся, или сообщение «Нет», если требуемых слов нет
C++11 в production, да или нет? C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 14:07     Coding style или нет #40
silent_1991, там вообще проблем нет. мы рассматриваем можно не можно.

Добавлено через 1 минуту
Цитата Сообщение от Evg Посмотреть сообщение
динамически выделяемую память, которую можно освободить только удалив экземпляр класса.
я как раз про это и писал в примере. когда в классе есть указатель на динамически созданный объект, а деструкторе его удаление и обнуление указателя.

конструктор с++ позволяет вызвать явно если это необходимо. explicit.

Добавлено через 4 минуты
Цитата Сообщение от Evg Посмотреть сообщение
Говоря про удобство и неудобство я имел в виду работу именно с ПЕРЕМЕННЫМИ типа класса, для которых компилятор АВТОМАТИЧЕСКИ вызывает конструкторы и деструкторы. Т.е. у тебя нет никакой возможности забыть вызвать деструктор
понятно. мне кажется в стринге в деструкторе как раз таки присутсвует код очищающий память динамического буфера и обнуляющий указатель. я это имел ввиду когда шла речь о стринге и о том, что требуется как то очистить его не используя { }.

Добавлено через 1 минуту
Цитата Сообщение от Evg Посмотреть сообщение
Но теперь
* // деструктор вызовется только в конце процедуры, а потребляемая string'ом память
* // всё это время будет висеть мёртвым грузом.
вот если от этого надо избавится, то можно использовать неудобное средство - вызвать деструктор. динамический буфер будет уничтожен. память таки осовбодится. другое дело что последующая попытка использования очищенного стринга может привести к ошибке. но ведь не об этом шла речь.
это наверно какой то случай мегаэкономии памяти должен быть, тогда лучше стринг наверно не использовать там
Yandex
Объявления
10.02.2012, 14:07     Coding style или нет
Ответ Создать тему
Опции темы

Текущее время: 16:09. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru