Приведение от неизвестного указателя к классу; как проверить действительность? - PullRequest
5 голосов
/ 04 июля 2011

Я использую LPARAM (по существу, long, если только мы не на 64-битной системе, в этом случае это long long) член LVITEM (называемый lParam) для сохранить указатель на объект, который совпадает с этой записью в ListView.

Когда я хочу отредактировать этот элемент, я хочу привести его LPARAM к MyClass*, что прекрасно работает, если lParam правильно установлен равным MyClass* в первую очередь , но я бы хотел сделать какую-то проверку, чтобы удостоверился, что на самом деле это MyClass, на которое указывает это число.

В настоящее время у меня есть это:

LVITEM lv;
// lv is filled in by LVM_GETITEM
classPtr = static_cast<MyClass*>((void*)lv.lParam);
if ( !classPtr )
    return false;

Теперь мне не совсем ясно: static_cast возвращает NULL, если аргумент не является допустимым указателем? Я предполагаю, что нет, поскольку именно для этого dynamic_cast, но я не совсем уверен. И, если я прав в этом предположении, есть ли способ проверить, что classPtr является действительным, прежде чем я попытаюсь получить доступ к его членам и вызвать сбой ...

Ответы [ 6 ]

5 голосов
/ 04 июля 2011

static_cast<> не выполняет проверку типов во время выполнения, поэтому то, что у вас есть, не будет работать, по крайней мере, не так, как вы думаете. Все, что static_cast<> делает - это арифметика указателей, основанная на информации, известной во время компиляции.

dynamic_cast<> может работать , но для этого требуется, чтобы в классе была хотя бы одна виртуальная функция (для RTTI), и он наверняка потерпит неудачу, если LPARAM даже не является указателем на что-то это тип класса.

Не существует общего способа определить, учитывая произвольный указатель или целочисленное значение, например LPARAM, что он фактически указывает на экземпляр MyClass.

Что бы я сделал в вашей конкретной ситуации - это обернуть представление списка в его собственный класс и предоставить методы, которые работают только с MyClass. Возможно так:

class MyListView
{
public:
    MyListView() { /* create the list view HWND */ }
    ~MyListView() { /* destroy the list view HWND */ }

    void Add(MyClass* listItem) // Only way to add items to list view
    {
        // Add to list view
    }

    MyClass* Get() const
    {
        LVITEM lv;
        // lv is filled in by LVM_GETITEM

        // Assume that this will work, since the only way to add
        // list view items is through Add(), and Add() only accepts
        // an instance of MyClass. Therefore the list view will only
        // have pointers to instances of MyClass.
        return static_cast<MyClass*>((void*)lv.lParam);
    }

    // ...

private:
    // Set to private so users can't modify
    // the list view without our consent.
    HWND listView;
};

Преимущество этого метода в том, что теперь у вас есть полный контроль над элементом управления представлением списка и его интерфейсом, и, таким образом, он всегда будет работать (несмотря на ошибки и злые / некомпетентные программисты). Вы даже можете сделать его шаблоном, чтобы он работал с классами, отличными от MyClass.

3 голосов
/ 04 июля 2011

Вы не можете проверить это вообще в чистом стандарте C ++. Вы можете выполнить только вероятностную проверку с помощью Windows API. Таким образом, на практике вы можете гарантировать это, только сделав свой код правильным.

Если бы вы могли проверить это, вы бы все равно не знали, что это был правильный MyClass экземпляр.

Итак, единственный хороший способ - сделать ваш код гарантированно правильным. Это проще, когда вы ограничиваете доступ к вещам. C ++ имеет множество функций, помогающих ограничить доступ, например, const; используйте их.

Приветствия & hth.,

2 голосов
/ 04 июля 2011

Если LPARAM не является указателем типа MyClass, а является произвольным битовым шаблоном, то и static_cast, и dynamic_cast будут показывать неопределенное поведение. dynamic_cast возвращает NULL, если указатель на действительный объект приведен к несвязанному классу, но он не поддерживает проверку наличия допустимого полиморфного объекта в определенной ячейке памяти.

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

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

почти нет возможности узнать. Вы должны создать некоторый родительский объект для всех ваших объектов (назовем его grand_dad), а затем унаследовать от него все ваши объекты. затем используйте изменение LPARAM на grand_dad* и используйте dynamic_cast<MyClass*>(lv.lparam).

Небольшое уведомление о динамическом и статическом приведении:

  • static_cast - это то, что почти всегда делает то, что вы просите, без всякой проверки!
  • dynamic_cast, с другой стороны, проверяет, является ли объект кастуемым или нет. если это не так, он возвращает нулевой указатель. но он использует таблицу виртуальных функций , чтобы проверить, что ваше приведение является допустимым, поэтому, если компилятор не знает, какую таблицу функций искать (например, если он приведен из void *), он всегда возвращает указатель NULL. если вы передаете некоторый экземпляр A и вызываете dynamic_cast, чтобы изменить его на isntance of B, и они не являются родительскими для друг друга, вы должны ожидать неопределенного поведения.
0 голосов
/ 04 июля 2011

На сегодняшний день самым простым решением является сохранение списка действительных MyClass объектов. В каждом ctor, добавьте this в список; в деструкторе вы удаляете его из списка.

Это не защитит вас от совпадений, но даже тогда у вас есть действительный MyClass*. Он даже охватывает производные классы, так как они вызывают базовые конструкторы.

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

Не нужно сбой.В Windows используйте структурированную обработку исключений .

Вы также можете сделать LONG magic_number первым полем MyClass и проверить его как дополнительную страховку, что это не какой-то другой объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...