Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Noexcept – предотвращение проброса исключений




Если функция не может генерировать исключение, или программа не рассчитывает на генерацию исключений функцией, то такая функция может быть объявлена с ключевым словом noexcept. Например:

// никогда не генерирует исключений

extern "C" double sqrt(double) noexcept;

// Мы не ожидаем нехватки памяти

vector<double> my_computation(const vector<double>& v) noexcept

{

// может сгенерировать исключение

vector<double> res(v.size());

for(int i; i<v.size(); ++i) res[i] = sqrt(v[i]);

return res;

}

 

Если функция, объявленная с ключевым словом noexcept все-таки сгенерирует исключение (т.е. исключение попытается покинуть функцию с noexcept), то программа закрывается (путем вызова функции terminate()). При вызове terminate() мы не можем рассчитывать на согласованное состояние объектов (т.е. нет гарантии того, что деструкторы вызваны, нет гарантии раскрутки стека, и нельзя возобновить выполнение программы, как будто ничего не произошло). Это намеренное поведение, которое делает noexcept простым, грубым и очень эффективным механизмом (значительно более эффективным по сравнению со старым механизмом throw()).

Спецификатор noexcept может быть условным. Например, некоторый алгоритм может не генерировать исключений, если (и только если) операции шаблонных параметров, используемых в этой функции также не генерируют исключения:

// do_f генерирует исключение, если f(v.at(0) может генерировать исключения

template<class T>

void do_f(vector<T>& v) noexcept(noexcept(f(v.at(0))));

{

for(int i; i<v.size(); ++i)

v.at(i) = f(v.at(i));

}

 

Здесь первое ключевое слово noexcept используется в качестве оператора: выражение noexcept(f(v.at(0))) равняется true, если f(v.at(0)) не генерирует исключений, т.е. если методы f() и at() объявлены с noexcept.

Оператор noexcept() является константным выражением и не вычисляет свои операнды.

Общая форма определения noexcept выглядит так: noexcept(expression) и «просто noexcept» является лишь сокращенной формой для noexcept(true). Каждое объявление функции должно содержать совместимую (compatible) спецификацию noexcept.

Деструкторы не должны генерировать исключения; сгенерированный деструктор неявно объявляется как noexcept (не зависимо от его тела), если деструкторы всех членов класса объявлены как noexcept.

Обычно операторы перемещения (move operators) не должны генерироватьисключений, поэтомустарайтесь объявлять их как noexcept. Сгенерированные компилятором операторы копирования и перемещения неявно объявлены как noexcept, если все члены, для которых используются операторы копирования и перемещения, содержат noexcept деструкторы.

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

См. также:

  • Standard: 15.4 Exception specifications [except.spec].
  • Standard: 5.3.7 noexcept operator [expr.unary.noexcept].
  • [N3103==10-0093] D. Kohlbrenner, D. Svoboda, and A. Wesie: Security impact of noexcept. (Noexcept must terminate, as it does).
  • [N3167==10-0157] David Svoboda: Delete operators default to noexcept.
  • [N3204==10-0194] Jens Maurer: Deducing "noexcept" for destructors
  • [N3050==10-0040] D. Abrahams, R. Sharoni, and D. Gregor: Allowing Move Constructors to Throw (Rev. 1).

 

Выравнивание (alignment)

Иногда, особенно при написании кода, работающего с сырой памятью, нам приходится для некоторого выделенного участка памяти указывать желаемое расположение (alignment). Например:

// массив символов, выровнен для хранения тиов double

alignas(double) unsigned char c[1024];

// выравнивание по 16 байтной границе

alignas(16) char[100];

 

Существует также оператор alignof, который возвращает выравнивание для указанного аргумента (аргумент должен быть типом). Например:

// целые числа выровнены по n-байтной границе

constexpr int n = alignof(int);

 

См. также:

  • Standard: 5.3.6 Alignof [expr.alignof]
  • Standard: 7.6.2 Alignment specifier [dcl.align]
  • [N3093==10-0083] Lawrence Crowl: C and C++ Alignment Compatibility. Aligning the proposal to C's later proposal.
  • [N1877==05-0137] Attila (Farkas) Feh�r: Adding Alignment Support to the C++ Programming Language. The original proposal.

 

Управление переопределением функций: override

Для переопределения функции базового класса, в классе наследнике не нужны никакие ключевые слова или аннотации. Например:

struct B {

virtual void f();

virtual void g() const;

virtual void h(char);

// Функция не виртуальная

void k();

};

struct D: B {

// переопределяет B::f()

void f();

// не переопределяет B::g() (тип неверен)

void g();

// переопределяет B::h()

virtual void h(char);

// не переопределяет B::k() (B::k() не виртуальная)

void k();

};

 

Такой код может приводить к неоднозначности (что имел в виду программист?) и проблемам, если компилятор не будет предупреждать о подозрительном коде. Например:

· Хотел ли программист переопределить B::g()? (наверняка, да).

· Хотел ли программист переопределить B::h(char)? (скорее всего, нет, поскольку он указал избыточное ключевое слово virtual).

· Хотел ли программист переопределить B::k()? (может быть, но это невозможно).

Теперь у нас есть ключевое слово override, которое позволяет программисту явно выражать свои намерения на счет переопределения функций.

struct D: B {

// OK: переопределяем B::f()

void f() override;

// ошибка: тип неверен

void g() override;

// переопределяет B::h(); скорее всего будет

// выдано предупреждение

virtual void h(char);

// ошибка: B::k() не виртуальная

void k() override;

};

 

Объявление функции с ключевым словом override является корректным, только если существует функция для переопределения. Проблема с методом h не обязательно будет отловлена (поскольку согласно определению языка, это не является ошибкой), но определить такую ситуацию легко.

См. также:

  • Standard: 10 Derived classes [class.derived] [9]
  • Standard: 10.3 Virtual functions [class.virtual]
  • [N3234==11-0004] Ville Voutilainen: Remove explicit from class-head.
  • [N3151==10-0141] Ville Voutilainen: Keywords for override control. Earlier, more elaborate design.
  • [N3163==10-0153] Herb Sutter: Override Control Using Contextual Keywords. Alternative earlier more elaborate design.
  • [N2852==09-0042] V. Voutilainen, A. Meredith, J. Maurer, and C. Uzdavinis: Explicit Virtual Overrides. Earlier design based on attributes.
  • [N1827==05-0087] C. Uzdavinis and A. Meredith: An Explicit Override Syntax for C++. The original proposal.

 

Управление переопределением функций: final

Иногда программист хочет предотвратить переопределение виртуальной функции. Теперь это можно сделать с помощью ключевого слова final. Например:

struct B {

// Функция не может быть переопределена

virtual void f() const final;

virtual void g();

};

struct D: B {

// ошибка: D::f пытается переопределить B::f

void f() const;

// OK

void g();

};

 

Существуют разумные причины, запрещающие переопределение функций, но, к сожалению, большая часть примеров, которые я приводил для обоснования необходимости final, основывались на ошибочном предположении, что вызов виртуальных функций является дорогим (в основном на основе опыта работы с другими языками программирования). Так что, если вы добавляете спецификатор final убедитесь в том, что причины обоснованы: будет ли семантическая ошибка в том, что кто-то захочет определить класс, переопределяющий эту виртуальную функцию? Добавление модификатора final, запрещает саму возможность того, что кто-то в будущем сможет предоставить более удачную реализацию вашей функции для класса, о котором вы даже не подозреваете. Если вы не хотите давать такую возможность, то зачем вообще объявлять эту функцию виртуальной? Большинство разумных ответов, с которыми я сталкивался, сводились к следующему: это важная функция фреймворка, она переопределяется разработчиками фреймворка, но не предназначена для переопределения простыми пользователям. Я к таким заявлениям отношусь с подозрением.

Если причина кроется в производительности (возможности inlining-а) или вы просто не хотите, чтобы ее кто-либо переопределял, то значительно проще вообще не делать эту функцию виртуальной. Это не Java.

См. также:

  • Standard: 10 Derived classes [class.derived] [9]
  • Standard: 10.3 Virtual functions [class.virtual]

 

Возможности С99

Для сохранения высокой степени совместимости, при содействии комитета по стандартизации языка С, в С++ были внесены несколько небольших изменений.

· long long.

· Extended integral types (например, правила для опциональных типов, больших чем int).

· UCN changes [N2170==07-0030] ``lift the prohibitions on control and basic source universal character names within character and string literals.''

· Конкатенация узких/широких строк.

· Не были внесены изменения VLA (Variable Length Arrays; слава небесам за это).

Были добавлены несколько расширений препроцессора:

· __func__ - максро, который разворачивается в имя лексически текущей функции

· __STD_C_HOSTED__

· _Pragrma: _Pragma(X) разворачивается в #pragma X

· макрос vararg (перегрузка макросов с разным числом аргументов)

#define report(test,...) ((test)?puts(#test):printf(_ _VA_ARGS_ _))

 

· пустые аргументы макросов

Были добавлены возможности стандартной библиотеки из C99 (по сути, все изменения С99 по сравнению с предшественником С89).

См.:

  • Standard: 16.3 Macro replacement.
  • [N1568=04-0008] P.J. Plauger: PROPOSED ADDITIONS TO TR-1 TO IMPROVE COMPATIBILITY WITH C99.

 

Расширенные целочисленные типы

Существует набор правил, как должны вести себя целочисленные типы высокой точности, если они существуют.

См.:

  • [06-0058==N1988] J. Stephen Adamczyk: Adding extended integer types to C++ (Revision 1).

 

Динамическая инициализация и разрушение в многопоточной среде

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

См.:

  • [N2660 = 08-0170] Lawrence Crowl: Dynamic Initialization and Destruction with Concurrency (Final proposal).

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...