views:

177

answers:

6

What are benefits of using these operators instead of implicit casting in c++?

dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression) 

Why, where, in which situation we should use them? And is it true that they are rarely used in OOP?

+3  A: 

Just as every other implicit thing, it may hide away logic that developer/reviewer didn't have in mind, masking away bugs.

Pavel Radzivilovsky
+7  A: 

From the list of casts you provided, the only one that makes sense to be used to supstitute an implicit cast is the static_cast.

dynamic_cast is used to downcast a superclass into its subclass. This cannot happen implicitly and is actually something that is not that rare in OOP. static_cast could be used in such a cast too, it is however more dangerous, as it does not check during run time that the downcast is valid.

The last cast, reinterpret_cast, should be used very carefully as it is the most dangerous of all. You can essentially cast anything into anything with that - but you as the programmer will have to make sure that such cast makes sense semantically, as you essentially turn off type checking by doing such cast.

inflagranti
I really must learn to type faster :-(
Chris Huang-Leaver
+2  A: 

Generally I have seen these type of casts appear in code when something doesn't build any more, possibly because we have started using a new compiler which is more strict about implicit conversions, so that's the key 'benefit' over implicit conversions. Obviously the correct thing to do in such a situation is to change the code in some other way!

Dynamic_cast can be used for casting 'upstream' with polymorphism. So if you have a structure like this;

Base -> Derived A

Base -> Derived B

you could do dynamic_cast(b); (b is a pointer to Base, but is actually a Derived_B) ;

If it wasn't a Derived_B class you will get a 0 returned instead of the converted pointer.

This is much slower than the static_cast as the checking is done at runtime, rather than compile time, but the intended use is different.

reinterpret_cast just changes the type label, enabling funky C-style FX (or 'type-punning' is its normally called), useful for protocol/ low level work, but should be used sparingly.

Generally lots of casts in code is an indication something is wrong with your code design.

Chris Huang-Leaver
A: 

Just want to add an example of a situation in which reinterpret_cast is used. Imagine a library gives you a pointer to a raw network packet. To make sense of the data-structure you can use structs, and then cast pointers to those structs. Note that there is no way for the compiler to verify whether you are doing something sensible here or just are going to read in memory where you shouldn't. In a real life situation you would first check the size of the packet to assert that it is big enough for your structs to fit in.

In the below code IPfragment constructor gets a packet and then casts that pointer into something sensible. I have added the definitions below.

If someone still thinks this use of reinterpret_cast is unjustified I'm glad to here about a better way to do it.

IPfragment::IPfragment( const byte* const pkt_data ) :
    ethernetHeader( reinterpret_cast< const EthernetHeader* >( pkt_data                    )  )
  , ipHeader      ( reinterpret_cast< const IPheader*       >( pkt_data + ETHER_HEADER_LEN )  )
  , payload       ( reinterpret_cast< const byte*           >( ipHeader                    )
                                                             + ( ipHeader->ver_hl & 0x0f ) *4 )
{
}

These are the definitions:

typedef uint8_t  byte  ;
typedef uint16_t word  ;
typedef uint32_t dword ;

#define ETHER_ADDR_LEN    6    // Ethernet addresses are 6 bytes
#define ETHER_HEADER_LEN  14   // Ethernet headers are 14 bytes
#define ETHER_TYPE_IP4    8 

struct EthernetHeader
{
   byte   etherDestHost[ETHER_ADDR_LEN];    // Destination host address
   byte   etherSrcHost [ETHER_ADDR_LEN];    // Source host address
   word   etherType;                        // IP? ARP? RARP? etc
};

/* 4 bytes IP address */
struct IPaddress
{
   byte byte1, byte2, byte3, byte4;
};

/* IPv4 header */
struct IPheader
{
   byte ver_hl          ;     // Version (4 bits) + Internet header length (4 bits)
   byte tos             ;     // Type of service
   word tlen            ;     // Total length
   word identification  ;     // Identification
   word flags_fo        ;     // Flags (3 bits) + Fragment offset (13 bits)
   byte ttl             ;     // Time to live
   byte proto           ;     // Protocol
   word crc             ;     // Header checksum
   IPaddress saddr      ;     // Source address
   IPaddress daddr      ;     // Destination address
   dword op_pad         ;     // Option + Padding
};

class IPfragment
{
  public:
    const IPheader*       const ipHeader;
    const EthernetHeader* const ethernetHeader;
    const byte*           const payload;

    IPfragment( const byte* const pkt_data );

    // rest of code omitted for brevity
}
ufotds
A: 

One of the benefits of using the C++ casts instead of C-style casts are that they are easily searchable. They also seperate out the different applications of the C-style casts, making smells easily identifiable.

For instance, a grep for reinterpret_cast could find you lots of potential problems and bad designs very easily, while a regex that correctly identifies C-style casts would need further inspection to identify the bad casts.

If a cast is necessary, I would always always use C++ casts and never C-style casts.

See C++ Coding Standards, Sutter and Alexandrescu, item 95.

tenpn
+2  A: 

For a very brief answer, the benefits of these casts is that they perform specific functions making the code descriptive. The C-style cast is all-powerful and allows you to get away with all kinds of mistakes. Those used to C might complain that the casts are a pain to write out. That's actually considered a good thing by others: it discourages programmers from sprinkling casts all over their code which is a very obvious sign of problematic code. Finally, they're easy to find with a text search.