5
votes

Okay, so this might be a bit of an academic question. Can someone tell me if/how C++'s casting operators might translate to Objective-C... or how/why they're not necessary?

I've been out of the loop with C++ for a few years now and it seems like every time I turn around they add a few new keywords. I was recently introduced to C++'s various casting operators reinterpret_cast, static_cast, dynamic_cast, and const_cast. I'm a little fuzzy on exactly when those situations come up that you would need to use all these types of casts.

I've been using Objective-C for a year or two now and feel fairly comfortable with it. (Been mostly a C person until then). I'm trying to understand why C++ seems to have all this complexity. Or to put it another way, what's Objective-C missing that it doesn't seem to have (or need?) this many casting types?

4
static_cast etc have been standardized in C++98 already, that's 12 years now...Pavel Minaev

4 Answers

9
votes

See this answer to the question When should static_cast, dynamic_cast and reinterpret_cast be used? on the meaning of each kind of casts.

what's Objective-C missing that it doesn't seem to have (or need?) this many casting types?

C++ focuses a lot more in type safety than C. The many cast operators are added to make the many different casting intentions clear (and to discourage people from using it due to its ugly form). And,

  • There is no const objects (const NSObject*) in Objective-C, and other const parameters aren't so emphasized unlike in C++, so const_cast is useless.

  • Objective-C instances always use dynamic typing, so dynamic_cast is not needed. (Type checking in ObjC is usually done with -isKindOfClass:.)

  • static_cast and reinterpret_cast are the same in C, but not so in C++. Because C++ supports multiple inheritance (missing in ObjC), a pointer casting is not as simple as a no-op:

    #include <cstdio>
    
    struct A {
        int x;
        A() : x(12) {}
    };
    struct B {
        int y;
        B() : y(33) {}
        int get() const { return y; }
    };
    struct C : A, B {
        int z;
        C() : A(), B(), z(41) {}
    };
    
    int main () {
        C* c = new C;
        printf("%d\n", c->get());                       // 33
        printf("%d\n", static_cast<B*>(c)->get());      // 33
        printf("%d\n", reinterpret_cast<B*>(c)->get()); // 12
    }
    
1
votes

Thing is, there are several broadly different categories of casts, with varying intent, and it is desirable to be able to explicitly specify that intent, so that you accidentally don't do the wrong thing. For example, you might be casting a const int* to const char* to operate on raw bytes, and unintentionally also drop the const. Consequently, in C++, changing the pointer from one unrelated type to another is done with reinterpret_cast, while casting away const can only be done with const_cast. If you try const void* p; reinterpret_cast<char*>(p), you'll get an error.

dynamic_cast is needed to reliably downcast things (with a runtime check), and also for cross-casts (i.e. given a pointer of type Base1* to an object of actual type Derived which inherits from both Base1* and Base2*, to be able to cast directly to Base2*; and the same for references - this is particularly useful for "interfaces").

static_cast is the rest of it - value conversions (int<->float etc), upcasting of object pointers/references, and unchecked downcasting. It is also the one used most often, though some style guides call for use of dynamic_cast on all object pointer/reference casts inasmuch as possible.

In general, the rationale seems to be: two most dangerous operations (casting to pointer-to-unrelated-type, and casting away constness) are provided with their own dedicated cast operators, to prevent any possibility of them occuring accidentally. Following that, a new operator is provided for cases where the cast requires runtime lookup. Finally, yet another new operator is provided to cover any remaining ground. The old, C-style "universal" casting operator is effectively deprecated.

Now as to why Obj-C doesn't "need" them. Arguably, it could actually use the difference between const_cast, reinterpret_cast and static_cast - but those were, apparently, seen as not adding enough value to extend the core C language. It doesn't need dynamic_cast, because it doesn't have multiple inheritance (protocols are somewhat different), and type checks are done via isa method instead.

1
votes

Where C (and Objective C) casts are heavy rubberized mallets, the C++ casts are a set of chisels. They are there to allow you to declare the specific kind of cast you'd like to make, and let the compiler help you enforce that specific kind of cast only. Since casts can be dangerous, the limited scope of each of these casts makes them safer – and the more awkward syntax is touted as an advantage, since it works to discourage overuse of casts.

The one that's quite different from Objective C is dynamic_cast, which does an additional runtime check to make sure the object is of the desired type before casting to it, and returning 0 or throwing an exception if it doesn't exist. It is similar to invoking isKindOfClass on an NSObject before doing an Objective C cast, and in the C++ world is very useful for downcasting. Note, however, that because of dynamic method dispatch in Objective C, you don't have to downcast an object before you can send it specific messages for its type, so the role of downcasting is less than it is in the C++ world.

1
votes

Have a look at the article on CPlusPlus.com which describes the various typecasting methods in detail.

  1. reinterpret_cast converts any pointer type to any other pointer type, even of unrelated classes. The operation result is a simple binary copy of the value from one pointer to the other. All pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked.
  2. static_cast can perform conversions between pointers to related classes, not only from the derived class to its base, but also from a base class to its derived. This ensures that at least the classes are compatible if the proper object is converted, but no safety check is performed during runtime to check if the object being converted is in fact a full object of the destination type.
  3. dynamic_cast can be used only with pointers and references to objects. Its purpose is to ensure that the result of the type conversion is a valid complete object of the requested class.
  4. const_cast is a type of casting which manipulates the constness of an object, either to be set or to be removed. For example, in order to pass a const argument to a function that expects a non-constant parameter

Broadly speaking, they allow the programmer to declare their intent and assert exactly what they're trying to achieve by casting. They also help flag up casting errors at compile time which would otherwise only show up at run time (if you're lucky!).

I've not used objective C myself, so I can't comment on why that language might not have them.