Скажи, что у меня есть void*
содержащий указатель на неизвестное class
, Я хочу использовать dynamic_cast
сделать проверку во время выполнения на тип класса, который я на самом деле. Например:
class Foo {};
void* bar = new Foo;
Если я попытаюсь сделать dynamic_cast<Foo*>(bar)
Я получил:
‘void *’: недопустимый тип выражения для dynamic_cast
Однако я необходимость dynamic_cast
потому что в моей реальной ситуации я не уверен, что bar
на самом деле Foo*
,
я прочел Вот что одним из решений этого является создание базового класса для всех объектов, которые bar
может содержать, reinterpret_cast
указатель на этот базовый класс, а затем попробуйте dynamic_cast
от этого объекта указатель на Foo
,
Это трудно для меня, потому что объекты, которые могут быть сохранены в bar
не все под моим контролем. (И потому что попытка воссоздать Java вызывает у меня изжогу.) Есть ли другой способ сделать это?
dynamic_cast
используется для приведения полиморфного объекта к классу с типом объекта, который вы пытаетесь привести в качестве его родителя.
void*
полностью отличается от этого.
с указателем на void вы буквально удаляете все типы данных.
dynamic_cast
знать, что есть базовый класс и может выполнять проверку типов через RTTI.
Когда вы отбрасываете пустой указатель, вы говорите компилятору: «Да, вы знаете это место в памяти? Хорошо, используйте его как этот тип», и если память недопустима, вызывается UB.
у вас есть три варианта здесь.
Опция 1
Используйте интерфейс.
Ну, полиморфный базовый класс является единственным способом сделать dynamic_cast
, Нет другого пути, нет хаков, это единственный путь. Просто как тот.
struct Base { virtual ~Base() = default; };
struct Derived : Base {};
// ...
void test (Base* base) {
auto derived = dynamic_cast<Derived*>(base);
if (derived) {
// derived is valid here.
}
}
Вариант 2
Определить тип с помощью указателя
Я использую метод, чтобы иметь уникальный идентификатор для каждого типа, и использую этот идентификатор для проверки приведения. Сделано без каких-либо RTTI
using type_id_t = void(*)();
template <typename T> void type_id() {}
// now we can use a map or a vector.
std::vector<std::pair<type_id_t, void*>> vec;
template<typename T>
void insertToMyVector(T* obj) {
vec.emplace_back(type_id<T>, obj);
}
template<typename T>
T* getObj(int index) {
auto item = vec[index];
return static_cast<T*>(item.first == &type_id<T> ? item.second : nullptr);
}
// ...
int main () {
auto foo = new Foo;
insertToMyVector(foo);
auto maybeFoo = getObj<Foo>(0);
if (maybeFoo) {
// you have a valid Foo here
}
}
Вариант 3
Создать производный класс для любого типа
Этот довольно полезен, поскольку он может содержать любой тип, сохраняя при этом безопасность типов. Я похож на решение 1, но предлагаю больше гибкости. Хитрость в том, чтобы сгенерировать производный класс для любого типа, используя шаблоны. Преимущество в том, что вы можете держать любой тип, но может немного усложнить вам задачу.
struct Base { virtual ~Base() = default; };
template<typename T>
struct Derived : Base {
Derived(T&& obj) : _obj{std::move(obj)} {}
Derived(const T& obj) : _obj{obj} {}
T& get() {
return _obj;
}
const T& get() const {
return _obj;
}
private:
T _obj;
};
// ...
void test (Base* base) {
auto derived = dynamic_cast<Derived<int>*>(base);
if (derived) {
int i = derived->get();
// derived is valid here, and we can safely access the int
}
}
Чтобы гарантировать, что dynamic_cast компилируется и работает, вы должны создать абстрактный или интерфейсный класс с виртуальным методом.
#include <cassert>
class Bar
{
public:
Bar() = default;
virtual ~Bar() = default;
};
class Foo : public Bar
{
public:
Foo() = default;
virtual ~Foo() = default;
};
int main()
{
Bar* bar = new Foo;
Foo* foo = dynamic_cast<Foo*>(bar);
assert(foo != nullptr);
}
Насколько я понимаю, вам нужен полиморфный объект, но нет общего базового класса.
Для этого уже существует довольно стандартная идиома — она называется boost::any
,
boost::any
несет ваш объект плюс некоторую информацию о типе. Интерфейс позволяет вам запрашивать тип и пытаться привести любой тип к искомому типу.