C++ `从基础到派生的动态_cast`

C++ `从基础到派生的动态_cast`,c++,dynamic-cast,C++,Dynamic Cast,是的,我知道如果Base不是多态的,那么使用dynamic\u cast的downcast无法编译,但我的问题不是这个 class Base { public: virtual void bar() { cout << "bar\n"; } }; class Derived: public Base { public: void foo() {

是的,我知道如果
Base
不是多态的,那么使用
dynamic\u cast
的downcast无法编译,但我的问题不是这个

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?
}
类基{
公众:
虚拟空心条()
{
cout foo();//也输出foo。为什么?
}
我认为当
pb=new-Derived;
时,
pb
实际上指向堆中的
Derived
对象。在
pd=dynamic_cast(pb);
之后,
pd
也指向该
派生的
对象,所以
pd->foo()
应该可以


但是当
pb=newbase;
时,
pb
指向的是堆中的
Base
对象,那么在
pd=dynamic_cast(pb);
之后,怎么可能
pd->foo()
works?是否
dynamic\u cast
将堆中的
Base
对象转换为
派生的
对象?

foo
中,您不访问
,在这种情况下,此
对象应为
NULL
。这就是无法执行转换时返回的内容


基本上,您处于“未定义的行为”区域。

您遇到了未定义的行为。您应该检查
动态\u cast的返回类型

pd = dynamic_cast<Derived*>(pb);  
pd=dynamic_cast(pb);

此返回null,并且调用一个函数,在<代码> null <代码>指针。任何事情都可能发生。C++中的

< p>,每个类的实例都有自己的数据类型版本,但是所有类在内存中都共享相同的函数(除了内联函数)。
pd->foo();
实际上,您正在调用
Derived::foo
,这是内存中的一个函数,编译器知道它在哪里。问题是,它根本不依赖于
pd
。但是,如果您有这样的函数:

class Derived : public Base {
    private:
        int a;

    public:
        Derived() { a = 100; }

        void foo() {
            std::cout<<a<<std::endl;
        }
 };
派生类:公共基{
私人:
INTA;
公众:
派生(){a=100;}
void foo(){

std::cout在尝试使用之前,请始终检查铸造是否成功。我想这是使用铸造的优点。您可以检查铸造是否成功

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

如果强制转换失败,
pd
的值为空。除非你确定它有值,否则不要使用
pd
。然后只对它进行反引用

FYI,因为它是正确的,所以我+1会告诉你答案。但我仍然不明白这样的事情是多么幸运。你可能会遇到难以跟踪的严重错误,因为程序在运行时没有崩溃应该有。@LuchianGrigore(n)1.偶然发生在某人身上的事情,偶然发生。@Alcott这是未定义的行为。任何事情都可能发生。@Alcott Luchian的回答在技术上是正确的。但实际上原因是编译器开发人员忽略这一点比较简单,因为他们是被允许的(因为标准没有规定在这种情况下该怎么做),而不是对每个指针访问执行复杂和性能消耗的运行时检查。因此,无论发生什么-发生什么,都是正确的。@Alcott所有未定义的行为都是正确的。只是不要这样做。@Alcott编写调用未定义行为的代码?是的,完全正确。编译器不负责教您这种语言:)编译器必须遵守标准,而标准并没有定义在这种情况下该做什么,所以从编译器的角度来看,它可以自由地做任何事情。作为程序员,您有责任检查可能出现空值的空值。当我说
pd->foo();
,然后是
foo()无论
pd
是否为
NULL
都将调用
。@Alcott是,但
参数将作为NULL传递(因为这是
pd
的值)因此,在Rohan的例子中,当您通过访问
a
来取消对它的引用时,会出现预期的崩溃。它很大程度上依赖于编译器,正如@Luchian Grigore所提到的,任何事情都可能发生。因此,在大多数情况下,是的,但这是您无法指望的。根据您的回答,您实际上是在调用
派生::foo
,而不是de完全依赖于pd,所以如果
pd==NULL
,那么
pd->foo()
应该像
NULL->foo()
,对吗?那么编译器怎么知道我要调用
派生::foo()
,而不是任何其他
foo()
?仅仅因为
pd
属于
派生类*
?是的。方法绑定到类类型,它们是一个方法,而不是实例。请这样想。当您有一个类似
pd->foo()
的调用时,编译器所做的事情类似于
Runtime.invoke(Derived.foo,pd)
。这不是实际情况,但我试图展示的是,方法的查找是基于类型而不是实例完成的。实例只不过是传递给方法的一个“参数”,也可能是
null