dynamic_cast для того же типа не проверяет тип объекта - PullRequest
1 голос
/ 01 июля 2011

Я пытаюсь определить, действительно ли объект, на который указывает указатель T *, является объектом T или каким-либо другим, не связанным типом.Я пробовал dynamic_cast, однако он менее чем бесполезен, он возвращает сам указатель вместо нуля, даже когда очевидно, что он не указывает на действительный объект T:

Object* garbage = reinterpret_cast<Object*>(0x12345678);
if( dynamic_cast<Object*>(garbage) == NULL ){
    cout << "Expected behaviour (by me)" << endl;
}else{
    cout << "You've got to be kidding me" << endl;
}

Есть ли обходной путь для этого,или какое-то другое решение?Я пробовал приводить к void * и char * перед dynamic_cast безрезультатно, typeid также недостаточно, поскольку я хочу также принимать подклассы.

Некоторый контекст: я пишу пользовательский класс Array, реализующийповерхностное преобразование между различными типами массивов, такими как Array<Object*> и Array<String*>, и я хотел бы гарантировать минимальную безопасность типов, выполняя динамическую проверку типов при каждом доступе к элементу, например:

#define DEBUG
Array<String*> v(10);
Array<Object*> o = v;
o[0] = new Integer(1);      //  this is technically illegal but no idea how to check
//Array<String*> w = o;     //  this fails with an exception
String* str = v[0];         //  but this should fail horribly as well
cout << str << endl;

Приведение к Object *, затем проверка типа для Object * работает во многих случаях, но в случае Array<Object*> происходит сбой, хотя я не уверен, возможно ли вставить что-то не-Object в Array<Object*> без использования reinterpret_cast.

Ответы [ 4 ]

1 голос
/ 01 июля 2011

Дайте мне посмотреть, следую ли я вашим потребностям и сделаю несколько советов по пути ...

Array<String*> v(10);

Похоже, это предназначено для того, чтобы дать вам массив с 10 String* s, инициализированными в NULL / 0.

Array<Object*> o = v;

Создает массив v.size() / 10 Object* с, каждый из которых скопирован из String*s in v.

o[0] = new Integer(1); //  technically illegal but no idea how to check

Если это недопустимо, то вы, очевидно, хотите предотвратить перезапись Object* s, когда это меняет тип времени выполнения ...

  • вам нужно перехватить operator= для реализации сравнения типов до / после
  • для перехвата operator=, вам нужно o[0] для возврата типа, чей operator= вы можете указать
    • разрешение o[0] вернуть объект * никогда не будет работать, так как указатели не являются пользовательскими классами: вы не можете изменить поведение operator=
    • у вас должно быть o [0], чтобы вернуть прокси-объект - здесь итератор, хотя утверждение семантики и типа присваивания отличается от стандартных контейнерных итераторов

Что приводит нас к:

Array<String*> w = o;     //  this fails with an exception

Я предполагаю, что это только сбой, потому что ваш o[0] = new Integer() выше не провалился первым, и что исключением является ваш преднамеренный тест на то, что типы элементов соответствуют ожиданиям: здесь нет проблем, если вы используете прокси-объект, как обсуждалось, для остановки целое число, попадающее в Array<Object*>.

String* str = v[0];     //  should fail horribly as well

Опять же, я предполагаю, что это не получится, потому что ваше раннее назначение Integer не сработало, и здесь нет новой проблемы.

cout << str << endl;

Итак, прокси-объект кажется ключевым. Дайте мне знать, если вы не знаете, как написать, но я предполагаю, что вы знаете ....

1 голос
/ 01 июля 2011

Исходя из вашего примера, звучит так, как будто у вас есть мелкие копируемые массивы, которые кто-то может обмануть, чтобы они содержали разные типы, чем те, которые должны содержаться.Я думаю, что «нормальным» решением этой проблемы было бы сделать это трудным для пользователей (то есть не предоставлять преобразования между Array<T> и Array<U>).Но, если вы настроены на свои идеи, я думаю, что это сработает:

template<typename Subclass>
class Array {
public:
    // ...
    Subclass *operator [] (size_t index) {
        assert( index < size_ );
        assert( dynamic_cast<Subclass*>(static_cast<Object*>(internal_[index])) != NULL );
        // ...
    }
    // ...
private:
    size_t size_;
    Subclass **internal_;
};

Вы можете сделать некоторую мета-магию шаблона и статическое утверждение, чтобы убедиться, что Subclass действительно является Подклассом Object (точно как это совсем другая тема).Как только это закончится, приведение к объекту *, а затем возврат к Subclass с dynamic_cast должен выполнить вашу цель.

0 голосов
/ 01 июля 2011

Просто введите общий базовый класс с виртуальным деструктором. С оптимизацией пустого основания это, вероятно, не добавит никаких издержек, и dynamic_cast просто заработает.

0 голосов
/ 01 июля 2011

A dynamic_cast для одного и того же типа определено как запрет на использование в C ++, поэтому он не может "потерпеть неудачу" в вашем примере. Вместо этого вы можете использовать оператор typeid.

Например, очень вероятно, что эта программа потерпит крах (что является «ожидаемым» результатом для получения информации о типе от объекта по случайному адресу):

int main()
{
    Object* garbage = reinterpret_cast<Object*>(0x12345678);
    if (typeid(*garbage) == typeid(Object))
        cout << "Your program thinks this garbage is an actual object!" << std::endl;
}
...