开发者

Casting from unknown pointer to class; how to check validity?

开发者 https://www.devze.com 2023-03-16 21:12 出处:网络
I am using the LPARAM (essentially a long, unless we\'re on a 64-bit system in which case it is a long long) member of an LVITEM (called lParam) to store a pointer to the object that coincides with th

I am using the LPARAM (essentially a long, unless we're on a 64-bit system in which case it is a long long) member of an LVITEM (called lParam) to store a pointer to the object that coincides with that entry in the ListView.

When I want to edit that item, I want to cast that LPARAM to a MyClass*, which works fine so long as the lParam is correctly set to be equal to a MyClass* in the first place, but I'd like to do some kind of checking to make sure it is, in fact, a MyClass that this number is pointing to.

Currently I have this:

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

Now, I'm not exac开发者_开发百科tly clear: does static_cast return NULL if the argument is not a valid pointer? I assume not, since that's what dynamic_cast is for, but I'm not entirely certain. And, if I am correct in this assumption, is there any way to check that classPtr is valid before I attempt to access its members and cause a crash...


static_cast<> performs no runtime checking of types, so what you have won't work, at least not the way you think it does. All static_cast<> does is pointer arithmetic based on information known at compile time.

dynamic_cast<> might work, but it requires that the class has at least one virtual function (for RTTI) and it'll surely fail badly if LPARAM isn't even a pointer to something that's a class type.

There is no general way to determine, given an arbitrary pointer or integral value like an LPARAM, that it actually points to an instance of MyClass.

What I would do in your specific situation is to wrap the list view in its own class and expose methods that works with MyClass only. Possibly like this:

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;
};

The advantage of this method is that now you have complete control over the list view control and its interface, and thus this will always work (bugs and evil/incompetent programmers notwithstanding). You can even make it a template so that it works with classes other than MyClass.


You can't check it at all in pure standard C++. You can only do a probabilistic check using the Windows API. So, in practice you can only guarantee it, by making your code correct.

If you could check it you would still not know that it was the correct MyClass instance.

So, the only good way is to make your code guaranteed correct. That's easier when you limit access to things. C++ has many features to help you limit access, e.g. const; use them.

Cheers & hth.,


If the LPARAM is not a pointer of type MyClass but rather an arbitrary bit pattern, both static_cast and dynamic_cast will show undefined behaviour. dynamic_cast returns NULL if a pointer to a valid object is cast to an unrelated class, but it does not support checking if there is a valid, polymorphic object at a particular memory location.

There is no smart way to check this, apart from maintaining a global list of valid pointers to MyClass* objects.


there is almost no way to find out. you have to create some parent object for all of you objects (let's call it grand_dad) and then inherite all your objects from it. then use change LPARAM to grand_dad* and use dynamic_cast<MyClass*>(lv.lparam).

a little notice about dynamic and static cast:

  • static_cast is somthing that almost always do what you ask for, no checking at all!
  • dynamic_cast on the other hand checks if the object is castable or not. if it isn't it returns NULL pointer. but it uses virtual function table to check your cast is legal so if the compiler doesn't know which function table to look (for example if it's casting from void*) it always return NULL pointer. if you pass some instance of A and call dynamic_cast to change it to isntance of B and they are not parent of each other you have to expect undifined behavior.


No need to crash. In Windows, use structured exception handling.

You can also make LONG magic_number the first field of MyClass and check it as extra insurance that it isn't some other object.


By far the easiest solution is to keep a list ov valid MyClass objects. In every ctor, add this to the list; in the destructor you remove it from the list.

This doesn't protect you against coincidences, but even then you have a valid MyClass*. It even covers derived classes, since they do call base constructors.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号