1

I am trying to determine whether an object pointed by a T* pointer is truly a T object, or some other, unrelated type. I tried dynamic_cast, however it is less than useless, it returns the pointer itself instead of null even when it is obvious it does not point to a valid T object:

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

Is there any workaround for this, or some other solution? I've tried casting to void* and char* before the dynamic_cast to no avail, typeid is not enough either since I want to accept subclasses as well.

Some context: I'm writing a custom Array class implementing shallow conversion between different kinds of Arrays, like Array<Object*> and Array<String*>, and I would like to guarantee a minimal type safety by doing a dynamic type check at every element access, for example:

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

Casting to Object*, then doing the type check on the Object* works in a lot of cases, but it fails in the case of Array<Object*>, though I am not sure whether it is possible to insert something non-Object into an Array<Object*> without the use of reinterpret_cast.

4

4 回答 4

1

Base on your example, it sounds like you've got shallow copy Arrays which someone could trick into containing different types than they are supposed to contain. I think the "normal" solution to this problem would be to make that difficult for users to do (i.e. don't provide conversions between Array<T> and Array<U>). But, if you're set in your ideas I think this will work:

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

You can do some template meta-magic and a static assert to make sure that Subclass is really a Subclass of Object (exactly how is a completely different topic). Once that is out of the way, casting down to an Object* and then back up to Subclass with a dynamic_cast should accomplish your goal.

于 2011-07-01T00:06:44.337 回答
1

Let me see if I'm following your needs, and makes some suggestions along the way...

Array<String*> v(10);

Seems this is meant to give you an array with 10 String*s initialised to NULL/0.

Array<Object*> o = v;

Creates an array of v.size() / 10 Object*s, each copied from the String*s in v.

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

If this is illegal, then you obvious want to prevent overwriting of Object*s where that changes the run-time type...

  • you need to intercept the operator= to implement the before/after type comparison
  • to intercept operator=, you need o[0] to return a type whose operator= you can specify
    • letting o[0] return an Object* will never work, as pointers aren't user-defined classes: you can't modify the operator= behaviour
    • you must have o[0] return a proxy object - here pretty much an iterator though the semantics and assignment type assertion is different from Standard container iterators

Which brings us to:

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

I assume this is only failing because your o[0] = new Integer() above wasn't failing first, and that the exception is your deliberate test that the element types meet expectations: no problem here then if you use a proxy object as discussed to stop the Integer getting into the Array<Object*>.

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

Again, I'm guessing this should fail because your earlier Integer assignment didn't, and there's no new problem here.

cout << str << endl;

So, the proxy object seems key. Let me know if you don't know how to write one, but I'm guessing you do....

于 2011-07-01T01:07:01.513 回答
0

A dynamic_cast from and to the same type is defined as a no-op in C++, so it cannot "fail" with your example. You can use the typeid operator instead.

For instance, this program is very likely to crash (which is the "expected" result for getting type information from an object at a random address):

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;
}
于 2011-06-30T23:40:56.130 回答
0

Just introduce a common base class with a virtual destructor. With the empty-base optimization, this probably won't add any overhead, and it will make dynamic_cast just work.

于 2011-06-30T23:54:10.623 回答