Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.0k views
in Technique[技术] by (71.8m points)

what is side-cast or cross-cast in Dynamic_cast in C++

What is side-cast/cross-cast in Dynamic_cast in C++. Can someone explain with an example?

#include <iostream>
using namespace std;

class A
{
    virtual void fn(){} 
};
class B:public A
{

};
class C:public A
{

};
int main() {

    B b;
    C* c;

    {
        c = dynamic_cast<C*> (&b);  
    }
    if(!c)
    {
        cout<<"invalid cast "<<endl;
    }
    return 0;
}

It prints invalid cast. So, what is side-cast?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The terms are pretty much explained in the comments, so I'm bundling that in my answer. But I think it is important to provide source code that shows the behavior of the code, since that provided by the asker only regards whether the crosscast compiles.

Assuming this inheritance tree (the dreaded diamond):

        BaseClass
       /         
      V           V
   Left          Right
               /
        V      V
       MostDerived

Crosscast

A crosscast or sidecast is when dynamic_cast<Left*>(pRight) returns a Left* that properly behaves as a Left*. This happens when pRight points to a MostDerived object. Crosscasts only work with dynamic_cast, not reinterpret_cast or static_cast.

Downcasting and Upcasting

Downcast is casting from a Right* to a MostDerived*, which may fail. When it could fail, this should be done with a dynamic_cast, which sets the result to nullptr to signal failure. A common pattern is to have a dynamic_cast in debug, but to use a faster static_cast in the corresponding release code, as in:

#ifdef NDEBUG
return static_cast<MostDerived*>(input);
#else
auto p = dynamic_cast<MostDerived*>(input);
assert(p != nullptr && "Unable to cast input to MostDerived*");
return p; 
#endif

Upcasting is done implicitly when you pass a MostDerived* where any of the base classes is expected. This succeeds because MostDerived* is always a Right*, Left* and BaseClass*. However, because of the dreaded diamond, casting a MostDerived* to BaseClass* directly is ambiguous and results in a compiler error.

So, in order to get a BaseClass* from a MostDerived*, we must first cast to Left* or Right* through an implicit upcast. The resulting Left* or Right* can be cast to the other side with a crosscast by using dynamic_cast. If using static_cast, the behaviour of the resulting pointer is incorrect when casting to Right*. This is due to the order that they are specified in MostDerived. This is because static cast does not change the pointer address, and therefore does not use the new vtable to resolve function calls.

For this behaviour, it does not matter whether virtual inheritance is specified.

The term crosscast is used in The C++ Programming Language, Fourth Edition by Bjarne Stroustrup, while sidecast is mentioned in point 5b of cppreference's explanation on dynamic_cast.

#include <iostream>

struct BaseClass { virtual const char * behave() = 0; };
struct Left : BaseClass { virtual const char * behave() { return "Left"; } };
struct Right : BaseClass { virtual const char * behave() { return "Right"; } };
struct MostDerived : Left, Right { };

int main()
{
    MostDerived * mostDerived = new MostDerived();
    // implicit upcast through the diamond results in a compile-time error, ambiguous:
    // BaseClass * baseClass = mostDerived;
    Left * left = mostDerived;
    BaseClass * baseClassThroughLeft = left; // or, equivalently:
    // BaseClass * baseClassThroughLeft = reinterpret_cast<Left*>(mostDerived);
    Right * right = mostDerived;
    BaseClass * baseClassThroughRight = right;
    
    // this is of course ambiguous and does not compile
    //std::cout << mostDerived->behave() << std::endl;
    
    // note, only the right has a pointer value of 8 more
    // the two BaseClass pointers point to the same as left,
    // as does mostDerived
    std::cout << "left:  " << left << std::endl << "right: " << right << std::endl 
              << mostDerived << std::endl << baseClassThroughRight << std::endl 
              << baseClassThroughLeft << std::endl;

    std::cout << "Cast Left BaseClass * expression to Right *" << std::endl;
    std::cout << "with static_cast, behaves as " 
              << static_cast<Right *>(baseClassThroughLeft)->behave() 
              << " at addr: " << static_cast<Right *>(baseClassThroughLeft) << std::endl;
    std::cout << "with dynamic_cast, behaves as "
              << dynamic_cast<Right *>(baseClassThroughLeft)->behave() 
              << " at addr: " << dynamic_cast<Right *>(baseClassThroughLeft) << std::endl;
              
    std::cout << "Cast Right BaseClass * expression to Left *" << std::endl;
    std::cout << "with static_cast, behaves as " 
              << static_cast<Left *>(baseClassThroughRight)->behave() 
              << " at addr: " << static_cast<Left *>(baseClassThroughRight) << std::endl;
    std::cout << "with dynamic_cast, behaves as "
              << dynamic_cast<Left *>(baseClassThroughRight)->behave() 
              << " at addr: " << dynamic_cast<Left *>(baseClassThroughRight) << std::endl;

    delete mostDerived;
    return 0;
}

The output of the program is:

left:  0xeffeb0
right: 0xeffeb8
0xeffeb0
0xeffeb8
0xeffeb0
Cast Left BaseClass * expression to Right *
with static_cast, behaves as Left at addr: 0xeffeb0
with dynamic_cast, behaves as Right at addr: 0xeffeb8
Cast Right BaseClass * expression to Left *
with static_cast, behaves as Right at addr: 0xeffeb8
with dynamic_cast, behaves as Left at addr: 0xeffeb0

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...