Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.82/11: Рейтинг темы: голосов - 11, средняя оценка - 4.82
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
1

Виртуальные методы без деструкторов

07.08.2019, 06:03. Показов 2152. Ответов 34
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Знаю что хочу странного, но все же. Можно ли как-то объявить класс с виртуальными методами. но без деструктора и чтоб компилятор не плевался варнингами? Средствами языка, а не отключив варнинги в компиляторе.

Предупреждая вопрос нафиг надо - хочу выделять память из пула, а потом вместо удаления отдельных объектов, грохнуть сам пул. Разумеется, чтобы это сделать, нужно чтобы все объекты были trivially destructible. А для этого у них не должно быть никакого деструктора.

Предупреждая возражение "а вдруг случайно какому-то потомку не тривиальный деструктор добавишь" - а я static_assert подопру.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//'Parent' has virtual functions but non-virtual destructor
class Parent
{
public:
    virtual void method(){}
};
 
class FirstChild:public Parent{};
class SecondChild:public Parent{};
 
union AnyChild
{
    AnyChild(){}
 
    FirstChild firstChild;
    SecondChild secondCHild;
};
 
template<typename T>
T*create(void*mem)
{
    static_assert (std::is_trivially_destructible<T>::value);
    return new (mem) T;
}
 
int main()
{
    AnyChild*pool=new AnyChild[100];
    //создание объектов
    create<FirstChild>(pool);
    create<SecondChild>(pool+1);
    //удаление пула
    delete[] pool;
    return 0;
}
1
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
07.08.2019, 06:03
Ответы с готовыми решениями:

Виртуальные методы
народ вот пример с виртуальными методами, и вывод этой программы class Ancestor { public: ...

Виртуальные методы
Здраствуйте , вот писал виртуальный метод доступа но почему то не получаеться , подскажите почему...

Виртуальные методы
Всем доброго времени суток! Вопрос следующий. Как я могу функцией делать подмену виртуального...

виртуальные методы
как вызвать виртуальный метод базового класса из наследуемого класса? struct A { virtual void...

34
Модератор
Эксперт С++
13507 / 10757 / 6412
Регистрация: 18.12.2011
Сообщений: 28,713
07.08.2019, 07:21 2
Цитата Сообщение от Renji Посмотреть сообщение
чтоб компилятор не плевался варнингами
А разве компилятору не по барабану, написали ли Вы деструктор или нет?
0
6579 / 4564 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
07.08.2019, 09:14 3
Цитата Сообщение от Renji Посмотреть сообщение
Предупреждая возражение "а вдруг случайно какому-то потомку не тривиальный деструктор добавишь" - а я static_assert подопру.
Можно не подпирать, он у тебя union не даст создать в этом случае.
Цитата Сообщение от Renji Посмотреть сообщение
Можно ли как-то объявить класс с виртуальными методами. но без деструктора и чтоб компилятор не плевался варнингами?
А какие у тебя ворнинги выдаёт?

Добавлено через 2 минуты
Сделай тогда уж
C++
1
2
3
4
struct AnyChild
{
    std::byte m_raw[std::max(sizeof(FirstChild), sizeof(SecondChild))];
};
А ещё лучше, воспользуйся std::variant или std:::any
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
07.08.2019, 09:41 4
oleg-m1973, std::alignment_storage лучше подойдет.
0
6579 / 4564 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
07.08.2019, 09:51 5
Цитата Сообщение от Croessmah Посмотреть сообщение
oleg-m1973, std::alignment_storage лучше подойдет.
И какой из типов туда передавать?

Добавлено через 7 минут
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
И какой из типов туда передавать?
А, ну да, ту да же размер передаётся. Тогда да, лучше вместо AnyChild использовать std::aligned_storage<std::max(sizeof(FirstChild), sizeof(SecondChild))>
0
Неэпический
17870 / 10635 / 2054
Регистрация: 27.09.2012
Сообщений: 26,737
Записей в блоге: 1
07.08.2019, 09:52 6
oleg-m1973,
C++
1
std::aligned_storage_t<std::max(sizeof(FirstChild), sizeof(SecondChild))>
Выравнивание будет задано по-умолчанию, правда, наиболее жесткое.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
07.08.2019, 10:26 7
Лучший ответ Сообщение было отмечено Renji как решение

Решение

Цитата Сообщение от Renji Посмотреть сообщение
без деструктора и чтоб компилятор не плевался варнингами? Средствами языка, а не отключив варнинги в компиляторе.
нет.

хорошая новость в том, что не нужно отключать ворнинг насовсем.
его можно отключить точечно.
https://rextester.com/LIAM80123

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#pragma once
#ifndef dTOOLS_PRAGMA_WARNING_USED_ 
#define dTOOLS_PRAGMA_WARNING_USED_ 1
 
#if 0
 
    // gcc
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #pragma GCC diagnostic pop
 
 
    #ifdef _MSC_VER
        #pragma warning( push )
        // warning C4521: 'Arg' : multiple copy constructors specified
        #pragma warning( disable : 4521)
    #endif
 
        // code...
 
    #ifdef _MSC_VER
        #pragma warning( pop  )
    #endif
 
#endif
 
//==============================================================================
//==============================================================================
 
#if defined(__GNUC__) || defined(__MINGW__) || defined(__MINGW32__)
    #define dWRN_UNUSED_LOCAL_TYPEDEFS   "-Wunused-local-typedefs"
    #define dWRN_UNUSED_VARIABLE         "-Wunused-variable"
    #define dWRN_UNUSED_FUNCTION         "-Wunused-function"
    #define dWRN_DELETE_NON_VIRTUAL_DTRO "-Wdelete-non-virtual-dtor"
    #define dWRN_NON_VIRTUAL_DTOR        "-Wnon-virtual-dtor"
    #define dWRN_CTOR_DTOR_PRIVACY       "-Wctor-dtor-privacy"
    #define dWRN_CONVERSION_NULL         "-Wconversion-null"
    #define dWRN_OLD_STYLE_CAST          "-Wold-style-cast"
    #define dWRN_CONVERSION              "-Wconversion"
    #define dWRN_EFFC_CPP_PLUS           "-Weffc++"
#elif defined(_MSC_VER)
    #define dWRN_UNUSED_VARIABLE         4100
    #define dWRN_UNREFERENCED_VARIABLE   4101
    #define dWRN_NON_VIRTUAL_DTOR        4265
    #define dWRN_NON_VIRTUAL_DTOR        4265
    #define dWRN_UNREACHABLE_CODE        4702
    #define dWRN_FORCE_VALUE_TO_BOOL     4800
#endif
 
//==============================================================================
//==============================================================================
 
#if defined(__GNUC__) || defined(__MINGW__) || defined(__MINGW32__)
 
    #define dPRAGMA_GCC_(x) _Pragma (#x)
 
    #define dTODO(x) \
        dPRAGMA_GCC_(message ("TODO: " #x))
 
    #define dOPTIMIZE(x) \
        dPRAGMA_GCC_(message ("optimize this function: " #x ))
 
    #define dPRAGMA_DISABLE_WARNING(warning_type) \
        dPRAGMA_GCC_(GCC diagnostic ignored warning_type)
 
    #define dPRAGMA_PUSH \
        _Pragma("GCC diagnostic push")
 
    #define dPRAGMA_POP \
        _Pragma("GCC diagnostic pop")
 
#elif defined(_MSC_VER)
 
    #define dSTRINGIFY_(a) #a
    #define dSTRINGIFY(a) dSTRINGIFY_(a)
 
    #define dTODO(x) \
        __pragma(message(__FILE__"(" dSTRINGIFY(__LINE__) "): " x))
 
    #define dOPTIMIZE(x) \
        dTODO("optimize this function: " #x )
 
    #define dPRAGMA_DISABLE_WARNING(warning_type) \
        __pragma(warning(disable: warning_type))
 
    #define dPRAGMA_PUSH \
        __pragma(warning(push))
 
    #define dPRAGMA_POP \
        __pragma(warning(pop))
 
#else
    error: compiler not support
#endif
 
#define dWTF dTODO("WHAT THE FUCK ?????")
 
//==============================================================================
//==============================================================================
 
#if 0
 
    // usage:
 
    dPRAGMA_PUSH
    dPRAGMA_DISABLE_WARNING(dWRN_UNUSED_VARIABLE)
    int main()
    {
        {
            int val = 10;
        }
 
        dTODO("hello");
        dOPTIMIZE("ololo");
        dOPTIMIZE(ololo);        
        
        dWTF;
    }
    dPRAGMA_POP
 
#endif
 
//==============================================================================
//==============================================================================
 
#endif // !dTOOLS_PRAGMA_WARNING_USED_
 
dPRAGMA_PUSH
dPRAGMA_DISABLE_WARNING(dWRN_NON_VIRTUAL_DTOR)
//  warning: ‘struct sample’ 
// has virtual functions and accessible non-virtual destructor [-Wnon-virtual-dtor]
 
struct sample
{
    virtual void foo() = 0;
};
 
struct der: sample
{
    virtual void foo() 
    {
        static_assert(
            sizeof(der) == sizeof(sample),
            "invalid size"
            
        );
    }
};
dPRAGMA_POP   
 
int main()
{
}
2
Комп_Оратор)
Эксперт по математике/физике
8950 / 4704 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
07.08.2019, 10:35 8
Renji, вопрос: а зачем union? Не лучше ли уже создавать unsigned char* блок. Размер наиболее протяженного потомка легко выясняется во время компиляции и передать как-то вроде new unsigned char [max_child_size*child_number] не вопрос?

И ещё. У меня не компилируется код если явно объявить деструктор базового как delete. В наследнике - можно. Хотя нигде не вызывается ни тот не другой.

Это - скользкий вопрос но:
И последнее. Если работать напрямую с памятью, то методы вызываются как виртуальные даже без наследования. Правда, пропадает связь с базовым классом напрочь. То есть, если последнее не нужно, то и наследование вроде как излишне. В этом разе достаточно совпадения сигнатур для вызова через указатель на "базовый" (класс публикующий интерфейс). Но смешивать наследников и не наследников уже нельзя будет.
Тут я сомневаюсь и с удовольствием послушаю мнения.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
07.08.2019, 16:43  [ТС] 9
Цитата Сообщение от hoggy Посмотреть сообщение
хорошая новость в том, что не нужно отключать ворнинг насовсем.
его можно отключить точечно.
Да, неплохое промежуточное решение.
Цитата Сообщение от IGPIGP Посмотреть сообщение
Renji, вопрос: а зачем union?
Просто код получается более читаемым чем километровый std::max для вычисления размера пула.
Цитата Сообщение от IGPIGP Посмотреть сообщение
И последнее. Если работать напрямую с памятью, то методы вызываются как виртуальные даже без наследования. Правда, пропадает связь с базовым классом напрочь.
Ну это понятно что если знать точный тип наследника, его методы можно дергать и без virtual. Причем, безотносительно к прямой работе с памятью.
Цитата Сообщение от zss Посмотреть сообщение
А разве компилятору не по барабану, написали ли Вы деструктор или нет?
Более старому по барабану, новый gcc уже ругается. И case без break он тоже, оказывается, не очень любит, хочет новомодный [[fallthrough]].
0
6579 / 4564 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
07.08.2019, 16:46 10
Цитата Сообщение от Renji Посмотреть сообщение
Просто код получается более читаемым чем километровый std::max для вычисления размера пула.
Ты не забывай, что хоть у твоих классов и нет деструкторов, конструкторы у них могут быть любые. В этом случае с union возникнет проблема.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
07.08.2019, 16:51  [ТС] 11
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Ты не забывай, что хоть у твоих классов и нет деструкторов, конструкторы у них могут быть любые. В этом случае с union возникнет проблема.
Вот для этого у union и прописан пустой конструктор. Если у union есть конструктор, элементам union также можно иметь конструктор. Типа, union разберется что и как конструировать.
0
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
07.08.2019, 16:59 12
Цитата Сообщение от Renji Посмотреть сообщение
Просто код получается более читаемым чем километровый std::max для вычисления размера пула
C++
1
2
3
4
5
6
7
8
9
10
11
12
//C++14
template<typename ... Args>
struct max_sizeof
{
    constexpr static size_t size = std::max({sizeof(Args)...});
};
 
template<typename ... Args>
constexpr size_t max_sizeof_v = max_sizeof<Args...>::size;
 
//...
max_sizeof_v<FirstType, SecondType, ThirdType>
3
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
07.08.2019, 17:03  [ТС] 13
Цитата Сообщение от HelicopterK52 Посмотреть сообщение
struct max_sizeof
Да, через вспомогательный шаблон действительно красивее. Только, наверно, не через структуру, а через функцию.
C++
1
2
3
4
template<typename...Args>constexpr std::size_t maxSize()
{
    return std::max(sizeof(Args)...);
}
0
829 / 253 / 34
Регистрация: 27.07.2016
Сообщений: 497
Записей в блоге: 1
07.08.2019, 17:09 14

Не по теме:

Цитата Сообщение от Renji Посмотреть сообщение
Только, наверно, не через структуру, а через функцию.
Это как барин пожелает. :)



Можно и без структуры и без функции:
C++
1
2
template<typename ... Args>
constexpr size_t max_sizeof_v = std::max({sizeof(Args)...});
2
Комп_Оратор)
Эксперт по математике/физике
8950 / 4704 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
07.08.2019, 17:52 15
Цитата Сообщение от Renji Посмотреть сообщение
Причем, безотносительно к прямой работе с памятью
Это я преобразовании указателя на участок не типизированный по способу выделения. То есть, тот случай что вы описали, но без union.

Добавлено через 6 минут
Цитата Сообщение от Renji Посмотреть сообщение
Ну это понятно что если знать точный тип наследника, его методы можно дергать и без virtual.
Как раз не важно. Важно иметь именованный метод с той же сигнатурой и он вызовется (или буде бум, если объявлено не в той же последовательности ).
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
07.08.2019, 18:03  [ТС] 16
Цитата Сообщение от IGPIGP Посмотреть сообщение
Как раз не важно. Важно иметь именованный метод с той же сигнатурой и он вызовется
Каким чудом при использовании parent->method вызовется метод с той же сигнатурой, но из Child? У нас же по условию задачи нет таблицы виртуальных функций. Соответственно, коду неоткуда узнать какую из возможных перегрузок дергать. Или имеется ввиду ручная реализация выбора перегрузки через switch(typeID)?
0
Комп_Оратор)
Эксперт по математике/физике
8950 / 4704 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
07.08.2019, 19:44 17
Цитата Сообщение от Renji Посмотреть сообщение
Каким чудом при использовании parent->method вызовется метод с той же сигнатурой, но из Child?
Обычным. Имена совпадают, сигнатуры и порядок объявления, общее количество (может быть тоже нужно) одинаково. Тогда через указатель любого близнеца (разнояйцевого - пардон за н6атурализм) можно вызвать соответствующий метод любого другого.

Добавлено через 3 минуты
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<iostream>
using namespace std;
 
struct BaseNoDestructable
{   
    BaseNoDestructable() {};
    virtual ///////
        void foo() { cout << "\nfooBase\n"; }
    //~BaseNoDestructable() = delete;//если не закомментировать - ошибка - ссылка на удалённую функцию  
};
 
struct ChildNoDestructable
    :public BaseNoDestructable////////
{   
    ChildNoDestructable() 
        :BaseNoDestructable()//////////
    {};
 
        void foo() { cout << "\nfooChild\n"; }
 
    //~ChildNoDestructable() = delete;
 
};
 
 
void TestThatMuck()
{
    const size_t pool_obj_len = 2;
    unsigned char *pool=new unsigned char[sizeof(BaseNoDestructable) * pool_obj_len];
    
    BaseNoDestructable* startObjPtr = reinterpret_cast<BaseNoDestructable*>(pool);
    new(startObjPtr)BaseNoDestructable();
    new(++startObjPtr)ChildNoDestructable();
    --startObjPtr;
    startObjPtr->foo();
    ++startObjPtr;
    startObjPtr->foo();
    delete[] pool;
 
}
 
int main()
{   
    TestThatMuck();
    return 0;
}
закомментируйте наследование (всё что помечно ///////////). Не уверен, что оно гарантированно, но работает.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
07.08.2019, 19:49  [ТС] 18
Цитата Сообщение от IGPIGP Посмотреть сообщение
Обычным. Имена совпадают, сигнатуры и порядок объявления, общее количество (может быть тоже нужно) одинаково. Тогда через указатель любого близнеца (разнояйцевого - пардон за н6атурализм) можно вызвать соответствующий метод любого другого.
Имена совпадают, а тела функций в FirstChild и SecondChild абсолютно разные. И никакой информации о том, какое тело подразумевалось, нет. Или все же имеется ввиду вызов с явным указанием подразумеваемого потомка?

Добавлено через 4 минуты
Цитата Сообщение от IGPIGP Посмотреть сообщение
закомментируйте наследование (всё что помечно ///////////). Не уверен, что оно гарантированно, но работает.
Закоментировал virtual. Чуда не случилось, оба раза вызвался базовый метод.
0
Комп_Оратор)
Эксперт по математике/физике
8950 / 4704 / 629
Регистрация: 04.12.2011
Сообщений: 13,999
Записей в блоге: 16
07.08.2019, 19:49 19
Цитата Сообщение от Renji Посмотреть сообщение
а тела функций в FirstChild и SecondChild абсолютно разные
-Изя, на углу Мойшу бьют!
-За что Абраша?
-За то что еврей...
-Но он же русский по паспорту!
-Они бьют не по паспорту.
Они не по телу вызываются. Компиллер ищет указатель на метод)
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
07.08.2019, 19:50  [ТС] 20
Цитата Сообщение от IGPIGP Посмотреть сообщение
Они не по телу вызываются. Компиллер ищет указатель на метод)
Где ищет? Нет virtual->нет таблицы в которой искать виртуальный метод.
0
07.08.2019, 19:50
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.08.2019, 19:50
Помогаю со студенческими работами здесь

Виртуальные методы
...в голове не укладываются. Помогите кто может :confusion: У меня про них сложилось...

Виртуальные методы
Дан родительский класс Animal (поле кличка). И два наследованных от него класса-потомка: Cat (поле...

виртуальные методы
#include&lt;iostream&gt; #include&lt;stdlib.h&gt; using namespace std; class MArray { protected:...

Наследование и виртуальные методы
В описании задания буквой А) обозначается задание на базовый класс, а буквой Б) - на класс...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru