Войти
Правильное резюмеСтатьи

нюансы по программированию на 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;
}

#C++

25 января 2011 (Обновление: 2 фев. 2013)

Комментарии [9]