нюансы по программированию на C++
Автор: Константин Бурлаченко
нюансы по программированию на С++
В этой статье представлены мои взгляды по вопросам языка C++, которые плохо освящаются во всех книгах, кроме книг Б. Страуструпа. Хвала, почёт, и уважение ему.) Лично мои взгляды развились именно из его книг.
Namespaces
Пространство имен (namespace) является механизмом отражения логического группирования. То есть если некоторые объявления можно объединить по какому-либо критерию, их можно поместить в одно пространство имен для отражения этого факта.
Преимущества пространство имён:
-отражение логической структуры
-исключение возможности конфликтов имён
-выражать логически связанный набор средств
-препятствовать доступу пользователей к ненужным им средствам
-не требовать значительных дополнительных усилий при использовании
Недостатки:
- трата времени на анализ отнесения объектов к различным пространствам имён
- различные дополнительные нюансы:
3-и важных нюанса:
- локальная переменная или переменная объявленная через using скрывает внешние переменные по отношению к блоку видимости
- когда библиотеки, объявляющие много имен, делаются доступными посредством using директивы, очень важно понимать, что конфликты неиспользуемых имен не рассматриваются как ошибки.
- элементы одного пространства имён могут находиться в различных файлах.
Оператор new (выделение памяти)
В С++ до появление механизма исключений оператор new возвращал 0, когда выделение памяти не удалось. В стандарте С++ new по умолчанию генерирует исключение bad_alloc.
Как правило лучше стремится к сходству со стандартом. Т.е. лучше изменить программу так, чтобы перехватывать bad_alloc, а не проверять на 0. В обоих случаях сделать что-то кроме выдачи сообщения об ошибке не так-то просто в большинстве систем.
Смотрите пункт 5.3.4. Подпункт 13.
http://www.ishiboo.com/~nirva/c++/C++STANDARD-ISOIEC14882-1998.pdf
Для компилятора Visual Studio:
1) new (std::nothrow) – не кидает исключение
2) обычный new - кидает исключение
3) можно настроить поведение с помощью опций линковщика:
http://msdn.microsoft.com/en-us/library/kftdy56f.aspx
Исключения
Когда программа конструируется из раздельных модулей и особенно когда эти модули находятся в независимо раздельных библиотеках обработка ошибок должна быть разделена на две части:
- генерация информации о возникновении ошибочной ситуации, которая не может быть разрешена локально
- обработка ошибок, обнаруженных в других местах
-Код обработки ошибок может быть короче и элегантней при использовании возвращающегося значения, говорящего об ошибочной ситуации, но это решение плохо масштабируется. Однако требуется больше модификаций при добавлении кода обработки ошибок к нормальному коду
-Как правило разделение кода обработки ошибок и “нормального” кода является хорошей стратегией
- Генерация исключения может оставить объект в недействительном состоянии.
- Генерация исключения может быть источником утечек памяти и других ресурсов из-за
отсутствия закрытия ресурса. Для правильного освобождения объекта лучше полагаться на свойства конструкторов и деструкторов и на их взаимодействие с обработкой исключений.(При выходе из блока с помощью генерации исключения происходит очистка всех созданных локальных автоматических объектов в обратном порядке создания.)
Также стандартная библиотека предоставляет шаблон auto_ptr
- Написать правильный безопасный при исключениях код с помощью явных try может оказаться трудной задачей.
Замечания:
- Можно группировать исключения, отношением наследования
- Исключения в момент генерации копируется. Модификатор const в блоке ни на что не влияет.
- Возможно повторно генерировать исключение
- При генерации исключения в конструкторе деструктор объекта не вызывается.
- Если во время вызова деструктора в процессе обработки исключения в деструкторе вызывается исключение – то это считается ошибкой механизма обработки исключения и вызывается std::terminate(). Для различия поведения можно воспользоваться в деструкторе вызовом uncaught_exception()
- Выход из деструктора через генерацию исключения является нарушением требований стандартной библиотеки.
- Если исключение сгенерировано, но не перехвачено вызывается std::terminate
Приоритет перегрузки функций/операторов
Приоритет перегрузки функций
1) Точное соответсвие типов, или соответсвие достигаемое тривиальным преобразованием (имя массива в указатель, имя функции в указатель на функцию, тип T в const T)
2) Соответсвие достигаемое путём продвижения интегральных типов (char в int, float в double)
3) Соответсвие достигаемое путём стандартных преобразований (int в double, double в int), указатели на производные в указатели на базовые класса. Указатели на произвольные типы в указатели на void*
4) Соответсвие достигаемое при помощи преобразований, определяемых пользователем
5) Соответсвие за счёт многоточия ...
Если соотвествие может быть достигнуто двумя способами на одном и том же уровне критериев, вызов считается неоднозначным и отвергается.
При перегрузке шаблонных функций ищется набор подходящях специалиазаций согласно шагам (1-4) указаным ниже
1) Ищутся все специализации, которые потенциально могут быть вызваны
2) Если какая-та специализация является более специализированной из двух, то менее специализированная отбрасывается
3) Разрешается перегрузка для функций с шага 1-2 и обычным функциям
4) Если обычная и шаблонная функция подходят одинакого хорошо приоритете отдаётся обычной функции
Если функции прошедшей 1-4 не найдено вызов считается ошибочным
Правила разрешения перегрузка бинарного оператора x@y. Где x имеет тип X, и y имеет тип Y
1) Если X есть класс. Выяснить определяется ли operator@ в качестве члена класса X или базового к X классу
2) Посмотреть объявление operator@ в контексте выражения x@y
3) Если X объвлен в пространстве имен N, поискать объвление оператора в пространстве имен N
4) Если Y объвлен в пространстве имен M, поискать объвление оператора в пространстве имен M
Правила поиска пространства имён
1) Если функция не найдена в контексте её использования то производится попытка поиска в пространстве имен аргументов.
Это правило не приводит к загрязнению пространства имен и экономит время.
namespace NameSpace {
struct Type{};
void func(Type x)
{}
}
...
func(NameSpace::Type());2) Локально объявленное имя скрывает нелокальное объвление. Выявляются все незаконные перегрузки.
3) Пространство имен могут быть вложенными. Для создания псеводнима можно использовать конструкцию типа
namespace AA = NameSpace::NameSpace2;
Ключевое слово typename
Как я знаю ключевое слово typename должно использоваться в трех задачах
1) Замена ключевого слова class на слово typename в декларации типов аргументов для шаблонного класса/функции/метода.
template <typename> struct S{}2) Обращения к именам типов через область видимость класса, являющегося агрументом шаблона
template <class T> struct S {
typename T::SomeType a;
}Комментарий Б. Страуструпа "В некоторых случаях умный компилятор мог бы догадаться...Но в общем случае это невозможно"
3) Ключевое слово typename необходимо, если имя типа зависит от параметров шаблона
template <class T> T findMax(const std::vector<T>& vec){
typename std::vector<T>::const_iterator max_i = vec.begin();
for (typename std::vector<T>::const_iterator i = vec.begin() + 1; i != vec.end(); ++i)
{
if (*i > *max_i)
max_i = i;
}
return *max_i;
}25 января 2011 (Обновление: 2 фев 2013)
Комментарии [9]