Есть ли динамическое связывание во время идиомы деинициализации

Ака: Есть ли какая-то идиома «Вызов виртуалов во время деинициализации»

Я очищаю старый код и мне нужно исправить случаи, когда виртуальные методы вызываются в конструкторах и деструкторах. Я не знаю кодовую базу, и она огромна. Основная перезапись не вариант.

Исправление для конструкторов было простым. Я переместил виртуальные звонки на статический Create шаблон и сделал все конструкторы защищены. Затем все, что мне нужно было сделать, это скомпилировать и изменить все местоположение, вызывающее ошибки, чтобы использовать Create шаблон. Минимальный шанс для регрессий. Однако для деструкторов аналога этому нет.

Как бы вы решили это?

Пример кода

#include <iostream>

class Base
{
public:
virtual ~Base()
{
DeInit();
}
protected:
virtual void DeInit()
{
std::cout << "Base" << std::endl;
}
};

class Derived : public Base
{
protected:
virtual void DeInit() override
{
std::cout << "Derived" << std::endl;
Base::DeInit();
}
};

int main()
{
Derived d;
}

Этот код не вызывает Derived::DeInit (печатает только «База»). Мне нужно исправить такие проблемы.

Рабочий пример кода

0

Решение

Это довольно сложно, так как деструкторы вызываются автоматически при выходе из областей, будь то при нормальном потоке, break, continue, return или же throw, Вот почему вы не можете передавать аргументы деструктору.

Простое решение состоит в том, чтобы позвонить Derived::DeInit от Derived::~Derived, Это имеет дополнительное преимущество по-прежнему Derived члены доступны.

Другой — создать свой собственный класс интеллектуальных указателей, который вызывает T::DeInit до T::~T, Чтобы избежать этого, верните этот умный указатель из вашего Create,

1

Другие решения

...
virtual Base::~Base()
{
Base::DeInit();
}
...

...
Derived::~Derived()
{
// de-initialization code
// do not call Derived::DeInit() here as otherwise Base::DeInit()
// will be called two times
}
...

и очистка вызовов виртуальных функций от деструкторов при их обнаружении.

1

Вам не нужно иметь виртуальное веселье DeInit.

    #include <iostream>

class Base
{
public:
virtual ~Base()
{
DeInit(); //this calls Base version
}
protected:
void DeInit()
{
std::cout << "Base" << std::endl;
}
};

class Derived : public Base
{

public:
~Derived()
{
DeInit(); //this calls Derived version
}
protected:
void DeInit()
{
std::cout << "Derived" << std::endl;
}
};

int main()
{
Derived d;
}

Выход:
Производный
База

это то, что вы хотели?

0

Решение, вдохновленное второй идеей MSalters.

Это решение требует только изменений в Base класс и к созданию Derived классы. Никаких изменений не требуется Derived реализация.

#include <iostream>
#include <memory>

class Base
{
private:
template <class T>
class WithAutoDeInit : public T
{
public:
virtual ~WithAutoDeInit() override
{
T::DeInit();
}
};

public:
template <class T>
static std::unique_ptr<typename std::enable_if<std::is_base_of<Base, T>::value, WithAutoDeInit<T>>::type> Create()
{
return std::make_unique<WithAutoDeInit<T>>();
}

virtual ~Base() = default;

protected:
virtual void DeInit()
{
std::cout << "Base" << std::endl;
}
};

class Derived : public Base
{
protected:
virtual void DeInit() override
{
std::cout << "Derived" << std::endl;
Base::DeInit();
}
};

int main()
{
Base::Create<Derived>();
}

Рабочий пример кода

Это не надежное решение. Вы все еще можете сделать примеры Derived непосредственно. И если вы обновите все свои Derived классы с защищенными конструкторами, которые не знающий разработчик может создать новый класс, забыв сделать свои конструкторы защищенными. Интересно, может ли это быть обеспечено каким-то утверждением в стратегическом месте?

static_assert(std::is_constructible<Derived>::value, "Derived class is constructable");

Кстати: я, наконец, решил переписать код. Я думаю, что это управляемо, и полученный код будет проще, потому что лучше.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector