2015-10-27 5 views
3

Скажите, что у меня есть void*, содержащий указатель на неизвестный class. Я хочу использовать dynamic_cast для выполнения проверки во время типа класса, который у меня есть на самом деле. Например:Проверка времени выполнения из-за пустоты *

class Foo {}; 

void* bar = new Foo; 

Если я пытаюсь сделать dynamic_cast<Foo*>(bar) я получаю:

'недействительным *': неверный тип выражения для dynamic_cast

Однако я нужноdynamic_cast, потому что в моем фактическая ситуация Я не уверен, что bar фактически является Foo*.

Я прочитал here, что одно решение этой проблемы является создание базового класса для всех объектов, которые могут содержать bar, reinterpret_cast на указатель базового класса, а затем пытаются dynamic_cast от этого указателя объекта для Foo.

Это сложно для меня, потому что объекты, которые могут храниться в bar, не все под моим контролем. (И причина попытки воссоздать Java дает мне изжогу.) Есть ли другой способ сделать это?

+4

Вы не можете ... –

+1

Тогда вам понадобится дополнительное состояние, чтобы рассказать вам, какой он тип. –

+2

Создайте интерфейс/абстрактный класс и объявите панель этого типа. –

ответ

0

Чтобы обеспечить компиляцию и работу 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); 
} 
+0

Но я думаю, что ваш комментарий больше, чем это право? В частности, дочерний элемент «Бар» должен быть оболочкой для другого объекта. –

+0

Это работает здесь, но не делает ничего, чтобы решить проблему, которую OP имеет, поскольку OP не имеет контроля над некоторыми объектами, переданными его функции. Поэтому он не может заставить их проистекать из общего объекта. – NathanOliver

0

Как я понимаю, вы хотите полиморфный объект, но не общий базовый класс.

Для этого уже существует довольно стандартная идиома - это называется boost::any.

A boost::any несет ваш объект и информацию о некоторых типах. Интерфейс позволяет вам запрашивать тип и пытаться применить любое к типу, который вы ищете.

http://www.boost.org/doc/libs/1_59_0/doc/html/any.html

+0

Ненавижу давать + 1s, чтобы поднять ответы, но это лучшая версия: http://stackoverflow.com/questions/33370296/run-time-checking-of-a-cast-from-a-void? noredirect = 1 # comment54535104_33370296 –

+1

Ненавижу давать импульсные ответы, но зачем изобретать совершенно круглое колесо? –

+0

:) Так что я не могу найти никакой реализации для этого, но мне очень любопытно, как это было сделано. Где-то я могу посмотреть на источник для этого? –

1

dynamic_cast является использование повергнуть полиморфный объект класса, который имеет тип объекта, который вы пытаетесь бросить, как это родитель.

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 
    } 
} 
+0

** 1 ** Был предложен в ссылке в вопросе и не является желательным решением. ** 2 ** Было предложено сначала [здесь] (http://stackoverflow.com/questions/33370296/run-time-checking-of-a-cast-from-a-void?noredirect1_comment54535104_33370296), но не очень хорошее решение для моей проблемы. ** 3 ** Описывается ли это решение [здесь] (http://stackoverflow.com/questions/33370296/run-time-checking-of-a-cast-from-a-void?noredirect1_comment54537066_33370296) и является наиболее перспективным до сих пор. –

+0

** 2 ** Может быть улучшено с помощью ['type_index'] (http://en.cppreference.com/w/cpp/types/type_index) и ** 3 ** Может быть улучшено путем добавления оператора литья вместо 'get':' operator T() {return _obj; } ' –

+0

Я бы предпочел добавить' operator * 'и' operator-> ', чем оператор преобразования. Или сделать оператор преобразования явным –