14

Yes, I know that downcast using dynamic_cast can't compile if the Base is not polymorphic, but my problem is not about this.

class Base {
    public:
        virtual void bar()
        {
            cout << "bar\n";
        }
};

class Derived: public Base {
    public:
        void foo()
        {
            cout << "foo\n";
        }
};

int main()
{
    Base *pb;
    Derived *pd;

    pb = new Derived;  //Base* points to a Derived object
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo();  //outputs foo

    pb = new Base;  //Base* points to a Base object
    pd = dynamic_cast<Derived*>(pb);  
    pd->foo();  //outputs foo, too. Why?
}

I thought when pb = new Derived;, pb actually points to a Derived object lies in heap. After pd = dynamic_cast<Derived*>(pb);, pd also points to that Derived object, so pd->foo() should be OK.

But when pb = new Base;, what pb points to is a Base object in heap, then after pd = dynamic_cast<Derived*>(pb);, how could pd->foo() works? Did dynamic_cast turn the Base object in heap into a Derived object?

Alcott
  • 17,905
  • 32
  • 116
  • 173

4 Answers4

20

In C++, each instance of a class has its own version of datatypes, but all classes share the same function in memory (other than for inline functions). In your case, when you say something like:

pd->foo();

You are essentially calling Derived::foo, which is a function in memory and the compiler knows where it is. The thing is, it is not dependent on pd at all. However, if you had something like this:

class Derived : public Base {
    private:
        int a;

    public:
        Derived() { a = 100; }

        void foo() {
            std::cout<<a<<std::endl;
        }
 };

Then, pd->foo() will cause a Segmentation fault. Here, your dynamic cast has failed and when Derived::foo is called, it is passed 0 as the this object. It was fine in the previous case, as the this object was never used. However, in the second case, it is used and hence, causes a Segmentation fault.

Rohan Prabhu
  • 7,180
  • 5
  • 37
  • 71
  • When I say `pd->foo();`, then `foo()` will get called no matter `pd` is `NULL` or not? – Alcott Apr 02 '12 at 09:25
  • @Alcott yes, but the `this` parameter will be passed as NULL (as that is the value of `pd`). Hence the expected crash when you dereferencing it by accessing `a` in Rohan's example. – littleadv Apr 02 '12 at 09:26
  • 1
    It is largely compiler dependent, and as @Luchian Grigore has mentioned, anything can happen. So, in most cases, yes, but it is something you cannot count on. – Rohan Prabhu Apr 02 '12 at 09:26
  • According to your answer, *You are essentially calling `Derived::foo`*, and *not dependent on pd at all*, so if `pd == NULL`, then `pd->foo()` should be like `NULL->foo()`, right? Then how did compiler know that I want to call `Derived::foo()`, not any other `foo()`? Just because `pd` is of type `Derived *`? – Alcott Sep 13 '13 at 07:57
  • Yes. Methods are bound to the class type they are a method of and not the instance. Think of it this way. When you have a call like `pd->foo()`, what the compiler does is something like `Runtime.invoke(Derived.foo, pd)`. This isn't what happens, but what I am trying to exhibit is that the lookup for the method is done based on the type and not the instance. The instance becomes nothing more than a 'parameter' which is passed to the method, which might as well be `null`. – Rohan Prabhu Sep 18 '13 at 09:33
8

In your foo you don't access this, which in this case should be NULL. That is what dynamic_cast returns when the cast cannot be done.

Basically you're in the "undefined behavior" area here.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • FYI, I +1'd your answer because it's correct. *But* still don't see how something like this is lucky. You can run into nasty bugs which are difficult to track because the program didn't crash when it should have. – Luchian Grigore Apr 02 '12 at 09:22
  • 1
    @LuchianGrigore [Luck](http://en.wiktionary.org/wiki/luck#Noun) (n) 1. Something that happens to someone by chance, a chance occurrence. – Potatoswatter Apr 02 '12 at 09:24
7

You're running into undefined behavior. You should check the return type of dynamic_cast.

pd = dynamic_cast<Derived*>(pb);  

This returns null, and you call a function on a NULL pointer. Anything can happen.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • @Alcott it's undefined behavior. Anything can happen. – Luchian Grigore Apr 02 '12 at 09:22
  • 1
    @Alcott technically Luchian's answer is correct. But practically the reason is because its simpler for the compiler developers to just ignore this, as they're allowed (because the standard doesn't define what to do in such case), instead of implementing complicated and performance-costing run time checks on every pointer access. Thus whatever happens - happens, and anything is correct. – littleadv Apr 02 '12 at 09:24
  • @Alcott all undefined behavior is. Just don't do it. – Luchian Grigore Apr 02 '12 at 09:28
  • @Alcott writing code that invokes undefined behavior? Yes, quite. Its not the compiler's responsibility to teach you the language:) Compiler has to adhere to standard, and standard doesn't define what to do in such case, so from the compiler's point of view its free to do whatever. Its your responsibility, as a programmer, to check for NULL where NULL can occur. – littleadv Apr 02 '12 at 09:28
1

Please always prefer to check whether the cast is successful before trying to use it. I guess its the advantage of using casting. You can check whether its successful or not.

pd = dynamic_cast<Derived*>(pb);  
if(pd!=NULL)
  pd->foo();

If the cast fails pd has value NULL. Do not use pd unless you are sure it has a value. and then only de reference it

Rohit Vipin Mathews
  • 11,629
  • 15
  • 57
  • 112