Russian Qt Forum

Программирование => С/C++ => Тема начата: 8Observer8 от Май 19, 2014, 18:36



Название: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: 8Observer8 от Май 19, 2014, 18:36
Привет! У меня есть такой код:

Код
C++ (Qt)
class LogicError : public std::logic_error {
public:
 
   LogicError( ) : std::logic_error( "" ) {
 
   }
 
   virtual const char *what( ) const throw( ) {
       return m_message.c_str( );
   }
 
   virtual ~LogicError( ) throw( ) {
 
   }
 
protected:
   std::string m_message;
};
 

Поясните, пожалуйста, почему компилятор требует:
1) Виртуальный деструктор
2) Наличие throw() в виртуальном деструкторе
3) Наличие throw() в методе what( )


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: Vamireh от Май 19, 2014, 19:48
потому что они перегружаются из класса logic_error и требуют точного соблюдения сигнатуры


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: 8Observer8 от Май 20, 2014, 05:18
Спасибо за ответ! Я так понял, что надо было мне смотреть на базовый класс под названием std::exception. А std::logic_error наследует what() и виртуальный деструктор: http://www.cplusplus.com/reference/exception/exception/

Причём компилятор заставляет переопределять виртуальный деструктор и пишет:
Цитировать
looser throw specifier for 'virtual LogicError::~LogicError()'
 class LogicError : public std::logic_error {
        ^

И я понял - почему. Если бы мы "понадеялись" на деструктор базового класса, то LogicError не был бы полностью уничтожен.

Вот только не могу понять, почему std::logic_error не переопределяет виртуальный деструктор? http://www.cplusplus.com/reference/stdexcept/logic_error/


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 09:30
Просто виртуальные методы (в отличии от чисто виртуальных) не обязательно переопределять - можно воспользоваться реализацией по-умолчанию, которая наследуется от предка. Но если мы все же хотим переопределить метод, то он должен полностью соответствовать родительскому по сигнатуре.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: 8Observer8 от Май 20, 2014, 09:44
С методами - да. А вот получается из примера выше, что виртуальный деструктор обязательно надо переопределять, хотя он и не чисто виртуальный.

Вопрос: почему std::logic_error не переопределяет виртуальный деструктор базового класса std::exception? http://www.cplusplus.com/reference/stdexcept/logic_error/

P.S. Хотя может просто не написали в справке. Либо я что-то не понимаю.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 09:55
Компилятор в ошибке говорит не о том, что надо обязательно переопределить деструктор, а о том, что ты неправильно его определяешь..
Если ты не переопределяешь деструктор, то будет использоваться деструктор, сгенерированный по умолчанию самим компилятором вот и все.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 10:08
Вот в таком случае деструктор надо обязательно переопределять
Код:
class A {
public:
    virtual ~A() = 0;

};
A::~A() {
    qDebug() << "Destroy A";
}

class B : public A {
public:
    ~B() {
        qDebug() << "Destroy B";
    }

};


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: 8Observer8 от Май 20, 2014, 10:47
Он мне выдаёт ошибку, если я не переопределяю.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 10:50
А какой компилятор?? Даже такой код не компилируется?  ???
Код:
class A : public std::logic_error{



};


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: 8Observer8 от Май 20, 2014, 11:28
Компилятор - тот что идёт со сборкой: ... for Windows 32-bit (MinGW 4.8.2, OpenGL)

Если в следующем примере раскомментировать код, то всё работает, с комментариями - нет.

Говорит, что:
Цитировать
main.cpp:6: error: looser throw specifier for 'virtual LogicError::~LogicError()'
 class LogicError : public std::logic_error {
        ^

Код
C++ (Qt)
 
#include <stdexcept>
#include <string>
#include <iostream>
 
class LogicError : public std::logic_error {
public:
 
   LogicError( ) : std::logic_error( "" ) {
 
   }
 
   virtual const char *what( ) const throw( ) {
       return m_message.c_str( );
   }
 
/*
   virtual ~LogicError( ) throw( ) {
 
   }
*/

 
protected:
   std::string m_message;
};
 
class EmptyArgument : public LogicError {
public:
 
   EmptyArgument( ) {
       m_message = "Error: empty argument";
   }
};
 
void printText( const std::string &text ) throw( EmptyArgument ) {
 
   if( text.empty( ) ) {
       throw EmptyArgument( );
   }
 
   std::cout << text << std::endl;
}
 
int main() {
   std::string text = "Hello, World!";
 
   try {
       printText( text );
   } catch( const LogicError &e ) {
       std::cerr << e.what() << std::endl;
   } catch( ... ) {
       std::cerr << "Error: unknown exception" << std::endl;
   }
 
   return 0;
}
 


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 11:56
Хм, дело вижу в std::string m_message;  Но вот почему, я не знаю, нужна помощь более опытных   :)


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 12:28
An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.

Вроде это. Деструктор по умолчанию будет зависеть от функций, которые ему придется вызывать при уничтожении объектов. Если они генерируют исключения,то и он будет и наоборот.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: 8Observer8 от Май 20, 2014, 13:36
А какие функции тут генерируют исключения? Такая запись throw( ) после метода означает, что он не будет генерировать никаких исключений.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 14:11
деструктор в std::string я думаю генерирует исключения.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: m_ax от Май 20, 2014, 14:42
деструктор в std::string я думаю генерирует исключения.
Дело не в том, генерирует или не генерирует  он исключения, а в том, что он (деструктор std::string) объявлен без throw(), в то время как деструктор LogicError однозначно исключения кидать не должен (семантика деструктора наследника должна совпадать с семантикой родителя). 
Можете проверить это, заменив string на свой пользовательский тип с пустым деструктором, который точно ничего не кидает, но без throw().. Будет таже самая ситуация, что и со string.
 


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: Old от Май 20, 2014, 14:43
http://stackoverflow.com/questions/18416780/overrriding-destructor-of-stdexception


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 14:58
деструктор в std::string я думаю генерирует исключения.
Дело не в том, генерирует или не генерирует  он исключения, а в том, что он (деструктор std::string) объявлен без throw(), в то время как деструктор LogicError однозначно исключения кидать не должен (семантика деструктора наследника должна совпадать с семантикой родителя). 
Можете проверить это, заменив string на свой пользовательский тип с пустым деструктором, который точно ничего не кидает, но без throw().. Будет таже самая ситуация, что и со string.
 

Ну это я и хотел сказать, что деструктор std::string может генерировать любые исключения в то время, как деструктор в примере вообще не должен их генерировать..


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: Old от Май 20, 2014, 15:07
Скажем так, деструктор string не может генерировать исключения, но это не декларированно явно, как у exception. :)
Поэтому, компилятор страхуется.


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: OKTA от Май 20, 2014, 15:58
Да, откопал про исключения string)
А если он никогда не генерирует исключения, почему тогда явно это не указано? Какой философский смысл?  ???


Название: Re: Зачем в данном коде виртуальный диструктор и throw()?
Отправлено: _Bers от Май 21, 2014, 00:39
Да, откопал про исключения string)
А если он никогда не генерирует исключения, почему тогда явно это не указано? Какой философский смысл?  ???

Да вообще нельзя кидать исключения в диструкторе. Это насмерть положит весь процесс.

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

Нынче - стандартную библиотеку допиливают под новый стандарт.
Если что-то ещё не допилили, то либо просто не успели. Либо - легаси.