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

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

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 23, средняя оценка - 4.65
HighPredator
 Аватар для HighPredator
5342 / 1725 / 320
Регистрация: 10.12.2010
Сообщений: 5,108
Записей в блоге: 3
09.02.2012, 19:56     Coding style или нет #1
Услышал сегодня от коллеги такую интересную вещь: есть блоки кода ограниченные командными скобками {}. Так вот, рекомендуется переменные, используемые в блоках и только в них, объявлять в таких блоках. Я например, как правило объявляю переменные в начале подпрограмм. Привычка. Вопрос такой: это чисто coding style рекомендация или есть какое-то практическое значение подобного действия?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
10.02.2012, 14:08     Coding style или нет #41
Цитата Сообщение от AzaKendler Посмотреть сообщение
я как раз про это и писал в примере. когда в классе есть указатель на динамически созданный объект, а деструкторе его удаление и обнуление указателя
Ну и чтобы инициализация-деинициализация была в руках программиста, нужно либо все экземпляры создавать динамически, либо заниматься онанизмом и использовать placement new (в качестве буфера можно использовать обычный локальный массив, не прибегая к динамической памяти). В обоих случаях всё сводится к тому, что нужно извращаться.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Bers
Заблокирован
10.02.2012, 14:10     Coding style или нет #42
Цитата Сообщение от Evg Посмотреть сообщение
... Но эти три строки кода будут работать долго. И всё это время память для уже ненужного объекта будет висеть занятой. Только потому, что это как бы красиво выглядит в исходнике
Ну ежели, это не критично для всего приложения - почему бы и нет? Они жить не мешают. А вот красивость - это бонус.

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

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

Иногда бывают особые моменты, когда могут потребоваться дополнительные временные объекты.
На самом деле требуются некие услуги, которые можно через них поиметь, а не сами объекты.
Можно просто сделать отдельную inline функцию, на которую свалить задачу по предоставлению данной услуги. Это оградит основной алгоритм от ненужных ему подробностей реализации, и избавит от присутствия совершенно не нужных ему временных объектов:

//здесь кучка кода
...
выходные_данные = foo(входные); //мелкая услуга
...
//остальной код
...
выходные_данные = foo(входные); //ещё какая нибудь мелкая услуга
...

С точки зрения "большого" куска кода, все объекты которые он используют - нужны.
Временных, или лишних не существует. Как работают мелкие поставщики услуг - их собственные трудности.
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 14:14     Coding style или нет #43
Цитата Сообщение от Evg Посмотреть сообщение
Ну и чтобы инициализация-деинициализация была в руках программиста, нужно либо все экземпляры создавать динамически, либо заниматься онанизмом и использовать placement new (в качестве буфера можно использовать обычный локальный массив, не прибегая к динамической памяти). В обоих случаях всё сводится к тому, что нужно извращаться.
C++
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
class Foo
{
    int* _ptr;
 
public:
    Foo(): _ptr(new int[10000])
    {
 
    }
 
    ~Foo()
    {
        delete [] _ptr;
        _ptr = 0;
    }
};
 
int main()
{
    Foo f;
 
//код
 
//а тут надо очень очень очень сэкономить и избавиться от того что висит в памяти. 
    f.~Foo();
//это все скорее неправильно и лучше сделать очищающую функцию, но речь об том как деструктором
//код
//код
//код
 
    return 0;
}
и правда не очень все это. но возможность есть
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
10.02.2012, 14:23     Coding style или нет #44
Цитата Сообщение от Bers Посмотреть сообщение
Ну ежели, это не критично для всего приложения - почему бы и нет? Они жить не мешают. А вот красивость - это бонус
А это и есть причина появления гавнокода. Тут оказалось не совсем критичным, там окзалось не совсем критичным. А потом наберётся этих ситуаций целая куча и программа начинает работать в два раза медленнее и жрать памяти в два раза больше

Цитата Сообщение от Bers Посмотреть сообщение
Если данному куску кода не нужен данный объект, то нет ни одной причины вообще создавать этот ненужный объект.
Дык проблема в том, что он был нужен чуть ранееи дальше появляется точка, где от объекта уже можно избавиться, т.к. по результату его использования было вычислено нужное значение, которое будет использовано в цикле из миллиона итераций. Я использую в примере string весьма условно (просто как некий экземпляр некоего класса)

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void func1 (int x1, int x2, int x3)
{
  std::string str;
 
  str += x1;
  str += x2;
  str += x3;
 
  int len = str.length();
 
  // Здесь str нам больше не нужен. Но поскольку у тебя принципиально нет никаких
  // переменных влексических блоках, то на время работы цикла память, отжираемая
  // экземпляром str, будет необоснованно болтаться как использованная
 
  for (i = 0; i < 1000000; i++)
    func2 (len);
}
Далее по твоей логике код работы с str надо выносить в отдельную функцию, в которую передать три параметра. Ну это в моём простом случае параметрв три, а может быть их и больше. Могут быть промежуточные значения, которые используются в виде локалов данной функции, которые придётся передавать в функцию. Могут быть несколько объектов типа str. И в итоге из-за принципиальных соображений один нормально читаемый код в виде одной нормально читаемой функции првреатится в десяток функций, в которых ориентироваться будет намного сложнее.

Добавлено через 4 минуты
Цитата Сообщение от AzaKendler Посмотреть сообщение
но возможность есть
Так твой код не рабочий. Он работает по счастливой случайности, что в дестркуторе ты вставил "_ptr=0", из-за чего повторный вызов "delete [] _ptr" не сломался. А ты воткни в деструктор печать, работу с файлом или чем-нибудь ещё, что имеет внешнее проявление. И увидишь, что завершающие действия у тебя выполняются два раза вместо одного
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 14:32     Coding style или нет #45
Evg
Так твой код не рабочий. Он работает по счастливой случайности, что в дестркуторе ты вставил "_ptr=0", из-за чего повторный вызов "delete [] _ptr" не сломался. А ты воткни в деструктор печать, работу с файлом или чем-нибудь ещё, что имеет внешнее проявление. И увидишь, что завершающие действия у тебя выполняются два раза вместо одного
он рабочий только потому что обнулен указатель. да деструктор вызовется 2 раза. и 2 раза выйдет на печать бодяга.
но ты ведь вел речь об экономии памяти в стринге ну и о том что он будет висеть до конца процедуры. для этого там есть clear(). и динамическая память очистится. с деструкторами это уже так полезли не в ту сторону. изначальная мысль - в с++ нет удобных средств для очистки памяти пока идет выполнение программы со строкой из твоего примера. возражаю они ЕСТЬ, это либо предусмотренные функции очистки в библиотченых классах. либо кривизна с деструктором.
ну а сам по себе стринг много места не занимает - ты сам писал. и уж если и это критично то можно не использовать стринг.
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
10.02.2012, 14:34     Coding style или нет #46
AzaKendler, никогда не обнулял указатель в деструкторе. Потому что не для того деструктор предназначен, чтобы когда нам заблагорассудится вызывался. Он должен вызываться для одного объекта один раз, при уничтожении, и точка. Это как с const_cast. Он вроде как есть, и его даже вроде как можно использовать, но если из-за него программа свалится в рантайме, то программист сам дурак.
Ну и плюс к тому, в своих классах можно обо всём позаботиться (напихать ключей, которые будут говорить, закрыт ли файл, разорвано ли соединение, очищена ли память, и проверять всё это в деструкторе), но проблема в том, что мы чаще используем чужой код, а не собственный. И при этом надо знать, что деструктор не предназначен для вызова вручную, и что если такой вызов будет иметь и место и затем будет иметь место падение в рантайме, то программист снова сам дурак.
Bers
Заблокирован
10.02.2012, 14:36     Coding style или нет #47
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int GetLenth(int x1, int x2, int x3) 
{ 
    std::string str; str +=x1; str +=x2; str +=x3; 
    return str.size(); 
}
 
void func1 (int x1, int x2, int x3)
{
  int len = GetLenth(x1,x2,x3);
 
  // Здесь str нам больше не нужен. 
 
  for (i = 0; i < 1000000; i++)
    func2 (len);
}
Цитата Сообщение от Evg Посмотреть сообщение
Могут быть промежуточные значения
Ну вот, по поводу промежуточных значений...

Иногда, исключительно для читабельности, есть смысл завести промежуточный объект.
Но это переменные типа int какие нибудь...

Если же в качестве такого промежуточного объекта просится очень тяжелый объект - явно что то не в порядке в самой архитектуре.

Пример того, чего быть не должно:

MyClass a,b; //оч тяжелые объекты
...
MyClass c = a; a=b; b=c;

Пример того, что должно быть:
a.Swap(b); //перенацелятся лишь парочка указателей. Никакого копирования.

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


Цитата Сообщение от Evg Посмотреть сообщение
И в итоге из-за принципиальных соображений один нормально читаемый код в виде одной нормально читаемой функции превратится в десяток функций, в которых ориентироваться будет намного сложнее.
Этого не может быть. Есть только два варианта:
1. Весь код представляет собой монолитную размазню.
2. Код разбит на функции.

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

Можно написать все в теле одной мега-раздутой функции. Можно разбить на десяток мелких.
Но само по себе "количество" кода либо не изменится, либо уменьшится, но только в случае разбиения на функции.
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 14:36     Coding style или нет #48
silent_1991, немного смысл утерян диалога. я выше в посте пометил почему началась эта тема.
конечно деструктор не нужно дергать дела без дела, а нужно только при необходимости/
А обнулять указатели надо по любому. поскольку в с++ delete нулевого указателя - это не ошибка, просто ничего не делается
а вот не обнуленного но уже ссылающегося на очищенную память - краш
Vourhey
Почетный модератор
6468 / 2243 / 123
Регистрация: 29.07.2006
Сообщений: 12,635
10.02.2012, 14:45     Coding style или нет #49
Evg, Bers прав... Если функция настолько велика, что вынуждает объявлять переменные в ее середине (без явных на то оснований), то это уже "говнокод". От величины и сложности проекта не зависящий.
Цитата Сообщение от Evg Посмотреть сообщение
один нормально читаемый код в виде одной нормально читаемой функции првреатится в десяток функций
Лучше 10 вызовов функций с говорящими именами, чем простыня на 100 строк в одной функции, из которых 40 на комментарии.
Цитата Сообщение от Evg Посмотреть сообщение
то на время работы цикла память, отжираемая
* // экземпляром str, будет необоснованно болтаться как использованная
Это никак не скажется на работе программы. (если только в следующих вызовах не соберемся стек переполнить, что получится сделать немного быстрее)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
10.02.2012, 14:52     Coding style или нет #50
Цитата Сообщение от AzaKendler Посмотреть сообщение
но ты ведь вел речь об экономии памяти в стринге
string я использовал как пример экземпляра класса для наглядности. На его месте может быть любой самодельный класс, активно потребляющий память и работающий с файлами

Bers, вот тебе фрагмент отладчика gdb: файл infrun.c. См. функцию handle_inferior_event. Это одна большая логическая последоваттельность действий, которая потеряет всякую логику, будучи искусственно разбитой на маленькие запчасти

Добавлено через 1 минуту
Цитата Сообщение от Vourhey Посмотреть сообщение
Это никак не скажется на работе программы. (если только в следующих вызовах не соберемся стек переполнить, что получится сделать немного быстрее)
Я уже говорил, что string - условность. Это может быть экземпляр произсольного класса, который отожрал 10 мегабайт динамической памяти и которая освободится только при удалении объекта
Bers
Заблокирован
10.02.2012, 14:55     Coding style или нет #51
Цитата Сообщение от AzaKendler Посмотреть сообщение
А обнулять указатели надо по любому. поскольку в с++ delete нулевого указателя - это не ошибка, просто ничего не делается
а вот не обнуленного но уже ссылающегося на очищенную память - краш
Вот я так и не определился с мыслью: ошибка это, или нет - удалять по нулевому указателю.

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

А если бы программка тихо промолчала - стало быть, и не узнал бы я, что у меня прокрался баг.

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

Добавлено через 3 минуты
Bers, ну если ты предпочитаешь так проверяться то да. ты практикующий постоянно судя по всему. к тебе можно прислушаться.
но при тестах ты можешь и не найти ту ошибку которая приводит к крашу и не обнаружить какие то петли и повторные вызовы. это может произойти у клиента. ну а если будет 0, то будет работать пока ты готовишь апдейты или проводишь доп тесты.
ну это такая фантазия

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

Добавлено через 1 минуту
Цитата Сообщение от Bers Посмотреть сообщение
И ежели указатель уже ноль
все таки нулишь?
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16824 / 5245 / 319
Регистрация: 30.03.2009
Сообщений: 14,121
Записей в блоге: 26
10.02.2012, 15:04     Coding style или нет #53
Цитата Сообщение от AzaKendler Посмотреть сообщение
все таки нулишь?
Он нулит его только для того, чтобы поймать ситуацию повторного вызова деструктора. И это правильный способ.
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 15:05     Coding style или нет #54
Цитата Сообщение от Bers Посмотреть сообщение
Вот я так и не определился с мыслью: ошибка это, или нет - удалять по нулевому указателю
думаю не ошибка. указатель может быть установлен в 0 в конструкторе и ждать некой функции Init();
если инит не вызван то указатель так и будет 0, и в деструкторе будет delete 0. если инит отработает то будет что то удалено из
памяти в деструкторе как и задумывалось
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
10.02.2012, 15:05     Coding style или нет #55
AzaKendler, clear в том же стринге память не очищает. В векторе, например, тоже. Т.е. ни стринг, ни вектор вообще никогда не уменьшают размера. Пример:
http://liveworkspace.org/code/4df806...11d68864746f5c
Напомню, что capacity возвращает количество элементов, которое контейнер может вместить без очередного выделения памяти.
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 15:10     Coding style или нет #56
silent_1991, тогда resize(0). за клиар сорри. спасибо за уточнения уж если писать то точно.
имел ввиду принцип очистки до удаления. напутал функцию
Bers
Заблокирован
10.02.2012, 15:11     Coding style или нет #57
Цитата Сообщение от AzaKendler Посмотреть сообщение
все таки нулишь?
Есть два режима: дебаг, и релиз.
Дебаг - это версия для отладки. Релиз - это версия для клиента.

В дебаге ассерты срабатывают. В релизе, код проверок будит выброшен препроцессором и не будет скомпилирован.

Перед удалением у меня стоит ассерт, который сработает, если указатель нулевой.
А после удаления, соответственно, зануляет указатель.

Таким образом, в дебаге у меня будет проверка на попытку двойного удаления.
А в релизе никаких проверок вообще уже не существует. Но так как указатели зануляются, то попытка двойного удаления вреда не нанесет.

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

Тут есть свои нюансы: например, если объект в принципе имеет право "не выделять динамическую память". Допустим, при создании, он ещё пустой, и его указатель нулевой. То.. он имеет право делетить нулевой указатель.
А если объект при создании автоматически выделяет память - тогда не имеет.
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
10.02.2012, 15:17     Coding style или нет #58
AzaKendler, clear и есть resize(0). Невозможно уменьшить размер выделенной в строке памяти извне. Даже reserve(0) не поможет.

Добавлено через 1 минуту
Вру.
Можно так:
C++
1
2
str.resize(0);
str.reserve(0);
Добавлено через 1 минуту
Но это только со строкой. Вектор совершенно отказывается "расти вниз".
Bers
Заблокирован
10.02.2012, 15:18     Coding style или нет #59
silent_1991

C++
1
void ClearStr(std::string& obj) {   std::string tmp; tmp.swap(obj); }
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
10.02.2012, 15:19     Coding style или нет
Еще ссылки по теме:

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

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

Или воспользуйтесь поиском по форуму:
AzaKendler
 Аватар для AzaKendler
214 / 116 / 9
Регистрация: 30.05.2011
Сообщений: 1,772
10.02.2012, 15:19     Coding style или нет #60
silent_1991, чтож остается только swap с пустым классом
Yandex
Объявления
10.02.2012, 15:19     Coding style или нет
Ответ Создать тему
Опции темы

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