Russian Qt Forum
Ноябрь 23, 2024, 08:14 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Зачем в данном коде виртуальный диструктор и throw()?  (Прочитано 11485 раз)
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( )
Записан
Vamireh
Гость
« Ответ #1 : Май 19, 2014, 19:48 »

потому что они перегружаются из класса logic_error и требуют точного соблюдения сигнатуры
Записан
8Observer8
Гость
« Ответ #2 : Май 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/
« Последнее редактирование: Май 20, 2014, 05:36 от 8Observer8 » Записан
OKTA
Гость
« Ответ #3 : Май 20, 2014, 09:30 »

Просто виртуальные методы (в отличии от чисто виртуальных) не обязательно переопределять - можно воспользоваться реализацией по-умолчанию, которая наследуется от предка. Но если мы все же хотим переопределить метод, то он должен полностью соответствовать родительскому по сигнатуре.
« Последнее редактирование: Май 20, 2014, 09:33 от OKTA » Записан
8Observer8
Гость
« Ответ #4 : Май 20, 2014, 09:44 »

С методами - да. А вот получается из примера выше, что виртуальный деструктор обязательно надо переопределять, хотя он и не чисто виртуальный.

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

P.S. Хотя может просто не написали в справке. Либо я что-то не понимаю.
Записан
OKTA
Гость
« Ответ #5 : Май 20, 2014, 09:55 »

Компилятор в ошибке говорит не о том, что надо обязательно переопределить деструктор, а о том, что ты неправильно его определяешь..
Если ты не переопределяешь деструктор, то будет использоваться деструктор, сгенерированный по умолчанию самим компилятором вот и все.
« Последнее редактирование: Май 20, 2014, 09:59 от OKTA » Записан
OKTA
Гость
« Ответ #6 : Май 20, 2014, 10:08 »

Вот в таком случае деструктор надо обязательно переопределять
Код:
class A {
public:
    virtual ~A() = 0;

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

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

};
Записан
8Observer8
Гость
« Ответ #7 : Май 20, 2014, 10:47 »

Он мне выдаёт ошибку, если я не переопределяю.
Записан
OKTA
Гость
« Ответ #8 : Май 20, 2014, 10:50 »

А какой компилятор?? Даже такой код не компилируется?  Непонимающий
Код:
class A : public std::logic_error{



};
Записан
8Observer8
Гость
« Ответ #9 : Май 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;
}
 
« Последнее редактирование: Май 20, 2014, 13:51 от 8Observer8 » Записан
OKTA
Гость
« Ответ #10 : Май 20, 2014, 11:56 »

Хм, дело вижу в std::string m_message;  Но вот почему, я не знаю, нужна помощь более опытных   Улыбающийся
Записан
OKTA
Гость
« Ответ #11 : Май 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.

Вроде это. Деструктор по умолчанию будет зависеть от функций, которые ему придется вызывать при уничтожении объектов. Если они генерируют исключения,то и он будет и наоборот.
« Последнее редактирование: Май 20, 2014, 12:47 от OKTA » Записан
8Observer8
Гость
« Ответ #12 : Май 20, 2014, 13:36 »

А какие функции тут генерируют исключения? Такая запись throw( ) после метода означает, что он не будет генерировать никаких исключений.
Записан
OKTA
Гость
« Ответ #13 : Май 20, 2014, 14:11 »

деструктор в std::string я думаю генерирует исключения.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #14 : Май 20, 2014, 14:42 »

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

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.147 секунд. Запросов: 23.