Сообщение ссылка на базовый класс без вирт деструктора от 22.08.2023 15:18
Изменено 22.08.2023 17:07 Sm0ke
ссылка на базовый класс без вирт деструктора
До недавнего времени я считал, что если временный объект класса Наследник
привязать к ссылке на константный класс База
то чтобы конечный объект разрушился правильно необходимо в базе задать виртуальный деструктор.
Но оказывается даже если в базе нет никакого явного деструктора, то деструктор наследника всё равно вызовется в этом случае.
Вот тест: https://godbolt.org/z/qncnsce79
В нём за базу взята структура t_param
А за наследника t_error : t_param
Вот output:
Тобишь и по правой сслыке на базу, и по левой константной, — всё равно деструктор наследника отрабатывает.
И как параметр функции, и как throw База catch Наследник.
Это по стандарту? Или компиляторы импровизируют?
Кстати проверено на: clang, gcc, msvc
Если по стандарту, то зачем тогда спрашивается в std::exception деструктор сделан виртуальным?
Может есть какие use кейсы, где это необходимо?
привязать к ссылке на константный класс База
то чтобы конечный объект разрушился правильно необходимо в базе задать виртуальный деструктор.
Но оказывается даже если в базе нет никакого явного деструктора, то деструктор наследника всё равно вызовется в этом случае.
Вот тест: https://godbolt.org/z/qncnsce79
В нём за базу взята структура t_param
А за наследника t_error : t_param
#include <iostream>
#include <string_view>
using t_text = std::string_view;
using namespace std::literals::string_view_literals;
struct t_param
{
// data
t_text
status;
};
struct t_error : public t_param
{
t_error(t_text p) : t_param{p} {}
~t_error() { std::cout << "~t_error: " << this->status << '\n'; }
};
void go1(const t_param & p) { std::cout << "go1()\n"; }
void go2(t_param && p) { std::cout << "go2()\n"; }
int main()
{
const t_param & cv = t_error{"const t_param & cv"sv};
t_param && rv = t_error{"t_param && rv"sv};
go1(t_error{"go1(const t_param & p)"sv});
go2(t_error{"go2(t_param && p)"sv});
try
{
std::cout << "going to throw\n";
throw t_error{"throw"sv};
} catch( const t_param & e ) {
std::cout << "catch\n";
}
std::cout << "end\n";
return 0;
}
Вот output:
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
go1()
~t_error: go1(const t_param & p)
go2()
~t_error: go2(t_param && p)
going to throw
catch
~t_error: throw
end
~t_error: t_param && rv
~t_error: const t_param & cv
Тобишь и по правой сслыке на базу, и по левой константной, — всё равно деструктор наследника отрабатывает.
И как параметр функции, и как throw База catch Наследник.
Это по стандарту? Или компиляторы импровизируют?
Кстати проверено на: clang, gcc, msvc
Если по стандарту, то зачем тогда спрашивается в std::exception деструктор сделан виртуальным?
Может есть какие use кейсы, где это необходимо?
ссылка на базовый класс без вирт деструктора
До недавнего времени я считал, что если временный объект класса Наследник
привязать к ссылке на константный класс База
то чтобы конечный объект разрушился правильно необходимо в базе задать виртуальный деструктор.
Но оказывается даже если в базе нет никакого явного деструктора, то деструктор наследника всё равно вызовется в этом случае.
Вот тест: https://godbolt.org/z/qncnsce79
В нём за базу взята структура t_param
А за наследника t_error : t_param
Вот output:
Тобишь и по правой сслыке на базу, и по левой константной, — всё равно деструктор наследника отрабатывает.
И как параметр функции, и как throw Наследник catch База.
Это по стандарту? Или компиляторы импровизируют?
Кстати проверено на: clang, gcc, msvc
Если по стандарту, то зачем тогда спрашивается в std::exception деструктор сделан виртуальным?
Может есть какие use кейсы, где это необходимо?
привязать к ссылке на константный класс База
то чтобы конечный объект разрушился правильно необходимо в базе задать виртуальный деструктор.
Но оказывается даже если в базе нет никакого явного деструктора, то деструктор наследника всё равно вызовется в этом случае.
Вот тест: https://godbolt.org/z/qncnsce79
В нём за базу взята структура t_param
А за наследника t_error : t_param
#include <iostream>
#include <string_view>
using t_text = std::string_view;
using namespace std::literals::string_view_literals;
struct t_param
{
// data
t_text
status;
};
struct t_error : public t_param
{
t_error(t_text p) : t_param{p} {}
~t_error() { std::cout << "~t_error: " << this->status << '\n'; }
};
void go1(const t_param & p) { std::cout << "go1()\n"; }
void go2(t_param && p) { std::cout << "go2()\n"; }
int main()
{
const t_param & cv = t_error{"const t_param & cv"sv};
t_param && rv = t_error{"t_param && rv"sv};
go1(t_error{"go1(const t_param & p)"sv});
go2(t_error{"go2(t_param && p)"sv});
try
{
std::cout << "going to throw\n";
throw t_error{"throw"sv};
} catch( const t_param & e ) {
std::cout << "catch\n";
}
std::cout << "end\n";
return 0;
}
Вот output:
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
go1()
~t_error: go1(const t_param & p)
go2()
~t_error: go2(t_param && p)
going to throw
catch
~t_error: throw
end
~t_error: t_param && rv
~t_error: const t_param & cv
Тобишь и по правой сслыке на базу, и по левой константной, — всё равно деструктор наследника отрабатывает.
И как параметр функции, и как throw Наследник catch База.
Это по стандарту? Или компиляторы импровизируют?
Кстати проверено на: clang, gcc, msvc
Если по стандарту, то зачем тогда спрашивается в std::exception деструктор сделан виртуальным?
Может есть какие use кейсы, где это необходимо?