Дополнительные скобки скобки в коде С++
Иногда вы запускаете код с дополнительными скобками скобок, которые не имеют никакого отношения к области видимости, только для удобства чтения и предотвращения ошибок.
Например:
GetMutexLock( handle ) ;
{
// brace brackets "scope" the lock,
// must close block / remember
// to release the handle.
// similar to C# lock construct
}
ReleaseMutexLock( handle ) ;
Другие места, которые я видел:
glBegin( GL_TRIANGLES ) ;
{
glVertex3d( .. ) ;
glVertex3d( .. ) ;
glVertex3d( .. ) ;
} // must remember to glEnd!
glEnd() ;
Это приводит к ошибке компилятора, если мьютекс не освобожден (при условии, что вы помните как вызов}, так и Release()
).
- Это плохая практика? Почему?
- Если это не одно, может ли он изменить способ компиляции кода или сделать его медленнее?
Ответы
Ответ 1
Собственные фигурные скобки прекрасны, все, что они делают, ограничено, и вы ничего не замедляете. Его можно считать чище. (Всегда предпочитайте чистый код по быстрому коду, если он чище, не беспокойтесь о скорости до тех пор, пока вы не профилируете.)
Но в отношении ресурсов это плохая практика, потому что вы поставили себя в положение для утечки ресурса. Если что-либо в блоке бросает или возвращает, то вы мертвы.
Использовать управление ресурсами с привязкой к области (SBRM, также известное как RAII), которое ограничивает ресурс для области видимости с помощью деструктора:
class mutex_lock
{
public:
mutex_lock(HANDLE pHandle) :
mHandle(pHandle)
{
//acquire resource
GetMutexLock(mHandle);
}
~mutex_lock()
{
// release resource, bound to scope
ReleaseMutexLock(mHandle);
}
private:
// resource
HANDLE mHandle;
// noncopyable
mutex_lock(const mutex_lock&);
mutex_lock& operator=(const mutex_lock&);
};
Итак, вы получаете:
{
mutex_lock m(handle);
// brace brackets "scope" the lock,
// AUTOMATICALLY
}
У всех это будут ресурсы, чище и безопаснее. Если вы в состоянии сказать "Мне нужно освободить этот ресурс", вы сделали это неправильно; они должны обрабатываться автоматически.
Ответ 2
Брекеты влияют на область переменных. Насколько я знаю, это все, что они делают.
Да, это может повлиять на компиляцию программы. Деструкторы будут вызываться в конце блока, а не ждать до конца функции.
Часто это то, что вы хотите сделать. Например, ваш GetMutexLock и ReleaseMutexLock будут намного лучше кода на С++, написанных следующим образом:
struct MutexLocker {
Handle handle;
MutexLocker(handle) : handle(handle) { GetMutexLock(handle); }
~MutexLocker() { ReleaseMutexLock(handle); }
};
...
{
MutexLocker lock(handle);
// brace brackets "scope" the lock,
// must close block / remember
// to release the handle.
// similar to C# lock construct
}
Используя этот более стильный стиль С++, блокировка автоматически открывается в конце блока. Он будет выпущен при любых обстоятельствах, включая исключения, за исключением setjmp/longjmp или сбоя программы или прерывания.
Ответ 3
Это не плохая практика. Это не делает ничего медленнее; это всего лишь способ структурирования кода.
Получение компилятора для проверки ошибок и принудительного исполнения для вас всегда хорошо!
Ответ 4
Конкретное размещение { ... }
в вашем исходном примере служит исключительно как форматирование сахара, делая его более очевидным, когда начинается группа логически связанных утверждений и где она заканчивается. Как показано в ваших примерах, это не влияет на скомпилированный код.
Я не знаю, что вы подразумеваете под этим "это вводит ошибку компилятора, если мьютекс не освобожден". Это просто неправда. Такое использование { ... }
не может и не будет приводить к ошибкам компилятора.
Является ли это хорошей практикой, это вопрос личных предпочтений. Все нормально. Кроме того, вы можете использовать комментарии и/или отступы для указания логической группировки операторов в коде без каких-либо дополнительных { ... }
.
Существуют различные методы, основанные на определении области охвата, некоторые из которых были проиллюстрированы другими ответами здесь, но то, что вы имеете в своем OP, даже не отдаленно выглядит примерно так. Еще раз, то, что у вас есть в вашем OP (как показано), является исключительно привычкой форматирования исходного кода с избыточным { ... }
, не влияющим на сгенерированный код.
Ответ 5
Это не будет иметь никакого значения для скомпилированного кода, кроме вызова любых деструкторов в конце этого блока, а не конца окружающего блока, если только компилятор не будет полностью безумным.
Лично я бы назвал это плохой практикой; способ избежать ошибок, которые вы можете сделать здесь, - использовать ограниченное управление ресурсами (иногда называемое RAII), а не использовать подверженные ошибкам типографические напоминания. Я бы написал код как-то вроде
{
mutex::scoped_lock lock(mutex);
// brace brackets *really* scope the lock
} // scoped_lock destructor releases the lock
{
gl_group gl(GL_TRIANGLES); // calls glBegin()
gl.Vertex3d( .. );
gl.Vertex3d( .. );
gl.Vertex3d( .. );
} // gl_group destructor calls glEnd()
Ответ 6
Все, что улучшает удобочитаемость IMHO, является хорошей практикой. Если добавление фигурных скобок помогает с удобочитаемостью, то идите!
Добавление дополнительных фигурных скобок не изменит способ компиляции кода. Это не приведет к замедлению работы программы.
Ответ 7
Это гораздо более полезно (IMHO) в С++ с деструкторами объектов; ваши примеры находятся на C.
Представьте, если вы создали класс MutexLock:
class MutexLock {
private:
HANDLE handle;
public:
MutexLock() : handle(0) {
GetMutexLock(handle);
}
~MutexLock() {
ReleaseMutexLock(handle);
}
}
Тогда вы можете обладать этой блокировкой только кодом, который ему нужен, предоставив новую область с фигурными скобками:
{
MutexLock mtx; // Allocated on the stack in this new scope
// Use shared resource
}
// When this scope exits the destructor on mtx is called and the stack is popped
Ответ 8
Если вы вставляете код в фигурные скобки, вы должны, вероятно, разбить его на свой собственный метод. Если это единая дискретная единица, почему бы не маркировать ее и не нарушать ее функционально? Это сделает его явным, что делает блок, и люди, которые позже прочитали код, не должны будут выяснять.