4

我写了一段代码,但我对它的输出感到困惑:

#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;
}

输出是:

B::foo
D::disp

但据我所知,pb指向类型 B。并且其中没有命名函数disp()?那么,为什么它可以访问disp()D 类中的函数呢?

4

5 回答 5

4

由于disp()不访问类的任何成员,原则上与在全局命名空间中声明它而不是在类中声明相同,因此调用它没有负面影响,即使实例不是正确的班级。

你在做什么是将基类的指针向下转换为派生类的指针,即使它没有被初始化。如果disp()尝试访问 inD但不在 in 的类成员B,您可能会遇到段错误。

底线:static_cast除非您绝对确定指针实际上指向派生类的实例,否则不要用于向下转换。如果您不确定,您可以使用dynamic_castwhich 在不匹配的情况下失败(但 RTTI 存在开销,因此请尽可能避免使用它)。

dynamic_castnullptr如果转换不正确,将返回,如果std::bad_cast 转换引用则抛出异常,因此您将确定它失败的原因,而不是可能的内存损坏错误。

于 2012-09-02T21:16:29.133 回答
2

该行:

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

无论源指针是B*or ,都会进行强制转换D*。在您的情况下,结果将是指向错误类型对象的指针。方法disp将起作用,因为它没有使用类的任何数据成员或虚函数D。在更复杂的情况下,这将导致不稳定的行为或崩溃。

您的对象是多态的。您应该dynamic_cast改用。

于 2012-09-02T21:18:39.863 回答
1

我认为,在这种情况下,重要的是成员函数disp()不会隐藏在所有 type 对象中D。它是一个存在于一个地方的单一功能。而任何对象是否会尝试调用 calldisp()是由代码决定的。

无论您传递什么指针,static_cast都将生成编译器认为指向的指针。一旦你有了一个指向 的指针,编译器就会让你尝试调用.D Ddisp()

换句话说,static_cast不会保护您免于错误地投射指针。

于 2012-09-02T21:21:55.417 回答
1

B当您将指向分配为指向派生类的指针的对象的指针强制转换时,您做了一件非常糟糕的事情D。这是标准所说的,强调我的:

5.2.9 静态投射

如果“指向 cv1 B 的指针”类型的右值指向实际上是 D 类型对象的子对象的 B,则生成的指针指向 D 类型的封闭对象。否则,强制转换的结果是未定义的.

你已经通过这样做调用了未定义的行为static_cast。当程序调用未定义的行为时,编译器和运行时可以做任何事情并且仍然是合规的。

于 2012-09-02T21:29:45.957 回答
0

您需要了解各种 C++ 类型转换之间的区别

您在这里使用静态转换,这与说“我真的不在乎它是否真的属于那种类型 - 只是尽力而为”。

在这种情况下,您想知道您的指针是否实际上是您将其转换为的派生类型。您应该使用 dynamic_cast。仅当指针的类型正确时,此强制转换才会成功。这意味着,如果它失败,它将返回一个 NULL 指针。

您看到的行为是当您没有为工作使用正确的演员表时会发生什么,虽然您可以尝试解释它,但您确实应该避免这种情况,因为它属于未定义行为的领域。换句话说,您不能期望跨编译器甚至同一编译器的不同版本会产生相同的副作用。换句话说,避免它。

于 2012-09-02T21:33:34.693 回答