質問

I've wrote a piece of code, but I am confused with its output:

#include <iostream>

using namespace std;

class B{
public:
    virtual void foo() {cout << "B::foo" << endl;}
};

class D:public B{
public:
    virtual void foo() {cout << "D::foo" << endl;}
    void disp() {cout << "D::disp" << endl;}
};

void func(B *pb){
    D *pd1 = static_cast<D*>(pb);
    pd1->foo();
    pd1->disp();
}

int main(int argc, char *argv[])
{

    B* pb = new B();
    func(pb); 

    return 0;
}

The output is:

B::foo
D::disp

But as far as I know, pb points to type B. And there's no function named disp() in it? So, why could it get access to disp() function in class D?

役に立ちましたか?

解決

Since disp() doesn't access any of the members of the class, it is in principle the same as if it were declared in the global namespace instead of in class, so there are no negative side effects to calling it, even though the instance is not of the right class.

What are you doing is downcasting a pointer of a base class to a pointer of the derived class, even though it was not initialized as such. If disp() tried to access class members that were in D but not in B, you would probably run into segfaults.

Bottom line: don't use static_cast for downcasting unless you're absolutely sure the pointer is actually pointing to an instance of the derived class. If you're unsure, you can use dynamic_cast which fails in the event of mismatch (but there is the overhead of RTTI, so avoid it if you can).

dynamic_cast will return nullptr if the cast is incorrect or throw a std::bad_cast exception if it casts references, so you will know for sure why it fails instead of possible memory corruption bugs.

他のヒント

The line:

D *pd1 = static_cast<D*>(pb);

Will make the cast regardless if the source pointer is B* or D*. In your case the result will be pointer that points to an object of a wrong type. Method disp will work because it is not using any data member or virtual function of the class D. In more complex case this will lead to unstable behaviour or a crash.

Your objects are polimorphic. You should use the dynamic_cast instead.

What's important in this context, I believe, is that the member function disp() isn't tucked away inside all objects of type D. It's a single function that exists in one place. And whether any object will try to call call disp() is decided by the code.

Your static_cast will produce what the compiler considers a pointer to D regardless of what pointer you pass it. And once you have a pointer to D, the compiler will let you attempt to call disp().

Put another way, static_cast will not protect you from casting a pointer incorrectly.

You did something very bad when you cast a pointer to an object allocated as a B to a pointer to a derived class D. Here's what the standard says, emphasis mine:

5.2.9 Static Cast

If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

You've invoked undefined behavior by doing that static_cast. The compiler and runtime can do anything and still be compliant when the program invokes undefined behavior.

You need to understand the difference between the various types of C++ casts.

You're using a static cast here, which is the same as saying, "I don't really care if it's actually of that type or not - just try your best".

In this case, you want to know if your pointer is actually of the derived type you're casting it to. You should use a dynamic_cast. This cast will succeed only if the pointer is of the correct type. That means, that if it fails, it will return a NULL pointer.

The behavior you're seeing is what happens when you don't use the right cast for the job, which, while you can try to explain it, is something you really should avoid because it falls in the domain of undefined behavior. In other words, you cannot expect the same side effects across compilers or even different versions of the same compiler. In other words, avoid it.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top