Intereting Posts
std :: unique_ptr и указатель на указатель Максимальное уменьшение omp с хранением индекса Как я могу использовать Google Test для вызова определенных тестовых функций в определенных местах в функции main ()? Инициализация по умолчанию в C ++ Переопределение и перегрузка в C ++ Почему этот код C ++ 11 содержит rand () медленнее с несколькими streamами, чем с одним? Модель памяти C ++ 0x и спекулятивные нагрузки / хранилища Неопределенная ошибка ссылочного компоновщика при использовании библиотеки id3lib в приложении на C ++ Неблокирующая связь в MPI и MPI Wait Issue. Не вся информация передается правильно Понимание `std :: is_move_constructible` Для чего используется #pragma? Почему этот код врезался в упомянутые места? «Ошибка: неразрешенный внешний символ» всякий раз, когда я использую чистую виртуальную функцию Является ли деструктор рассмотренной функцией const? C ++: почему вызов std :: wstring :: begin () перед #include вызывает ошибку компилятора в этом коде?

Как спецификация исключения влияет на переопределение виртуального деструктора?

В стандарте C ++ указано следующее о виртуальных функциях, имеющих спецификации исключений:

Если виртуальная функция имеет спецификацию исключения , все декларации, включая определение любой функции, которая переопределяет эту виртуальную функцию в любом производном classе, должны допускать исключения, допускаемые спецификацией исключения для виртуальной функции базового classа (C + +03 §15.4 / 3).

Таким образом, плохо развивается:

struct B { virtual void f() throw() { } // allows no exceptions }; struct D : B { virtual void f() { } // allows all exceptions }; 

(1) Действует ли это правило для деструкторов? То есть следующие хорошо сформированные?

 struct B { virtual ~B() throw() { } }; struct D : B { virtual ~D() { } }; 

(2) Как это правило применяется к неявно объявленному деструктору? То есть следующие хорошо сформированные?

 struct B { virtual ~B() throw() { } }; struct D : B { // ~D() implicitly declared }; 

Хотя в общем случае никогда не следует писать спецификацию исключения , этот вопрос имеет практические последствия, поскольку std::exception destructor является виртуальным и имеет пустую спецификацию исключения.

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

    (1) Действует ли это правило для деструкторов?

    Да, это правило применяется к деструкторам (исключение из правила для деструкторов), поэтому этот пример плохо сформирован. Чтобы сделать его корректным, спецификация исключения ~D() должна быть совместима с спецификацией ~B() , например,

     struct B { virtual ~B() throw() { } }; struct D : B { virtual ~D() throw() { } }; 

    (2) Как это правило применяется к неявно объявленной специальной функции-члену?

    В стандарте C ++ говорится о неявно объявленных специальных функциях-членах:

    Неявно объявленная специальная функция-член должна иметь спецификацию исключения.

    Если f – неявно объявленный конструктор по умолчанию, оператор-конструктор копирования, деструктор или оператор копирования, его неявная спецификация исключения указывает тип-идентификатор T тогда и только тогда, когда T разрешено спецификацией исключения функции, непосредственно вызванной f ‘ неявное определение;

    f разрешает все исключения, если какая-либо функция, которую он вызывает напрямую, допускает все исключения, а f не допускает исключений, если каждая вызываемая им функция не допускает исключений (C ++ 03 § 15.4 / 13).

    Какие функции непосредственно вызывают неявно объявленный деструктор?

    После выполнения тела деструктора и уничтожения любых автоматических объектов, выделенных в теле, деструктор для вызовов classа X

    • деструкторы для прямых членов X ,
    • деструкторы для прямых базовых classов X и,
    • если X – тип самого производного classа, его деструктор вызывает деструкторы для виртуальных базовых classов X

    (C ++ 03 §12.4 / 6, переформатирован для упрощения чтения).

    Таким образом, неявно объявленный деструктор имеет спецификацию исключения, которая допускает любые исключения, разрешенные любым из этих деструкторов. Чтобы рассмотреть пример из вопроса:

     struct B { virtual ~B() throw() { } }; struct D : B { // ~D() implicitly declared }; 

    Единственный деструктор, называемый неявным объявлением ~D() равен ~B() . Поскольку ~B() позволяет исключений, ~D() допускает исключений, и это как бы объявлено virtual ~D() throw() .

    Эта спецификация исключений, очевидно, совместима с ~B() , поэтому этот пример хорошо сформирован.


    В качестве практического примера того, почему это имеет значение, рассмотрим следующее:

     struct my_exception : std::exception { std::string message_; }; 

    ~string() допускает все исключения, поэтому неявно объявленное ~my_exception() допускает все исключения. Деструктор базового classа, ~exception() , является виртуальным и не позволяет исключений, поэтому деструктор производного classа несовместим с деструктором базового classа, и это плохо сформировано.

    Чтобы сделать этот пример хорошо сформированным, мы можем явно объявить деструктор с пустой спецификацией исключения:

     struct my_exception : std::exception { virtual ~my_exception() throw() { } std::string message_; }; 

    Хотя эмпирическое правило никогда не должно писать спецификацию исключения, существует, по крайней мере, один общий случай, когда это необходимо.