我写了一段代码,但我对它的输出感到困惑:
#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()
的函数?那么,为什么它可以访问D类中的disp()
函数?
答案 0 :(得分:4)
由于disp()
不访问该类的任何成员,因此它原则上与在全局命名空间而不是在类中声明它相同,因此对调用没有负面影响它,即使实例不是正确的类。
你在做什么是将基类的指针向下转换为派生类的指针,即使它没有被初始化。如果disp()
尝试访问位于D
但不在B
中的类成员,则可能会遇到段错误。
底线:除非您完全确定指针实际指向派生类的实例,否则不要使用static_cast
进行向下转换。如果您不确定,可以使用dynamic_cast
,如果不匹配则会失败(但是有RTTI的开销,所以如果可以,请避免使用它。)
dynamic_cast
将返回nullptr
或投出std::bad_cast
如果它转换引用的异常,那么您将确定它失败的原因而不是可能的内存损坏错误。
答案 1 :(得分:2)
该行:
D *pd1 = static_cast<D*>(pb);
无论源指针是B*
还是D*
,都会进行转换。在您的情况下,结果将是指向错误类型的对象的指针。方法disp
将起作用,因为它没有使用类D
的任何数据成员或虚函数。在更复杂的情况下,这将导致不稳定的行为或崩溃。
你的物体是polimorphic。您应该使用dynamic_cast
代替。
答案 2 :(得分:1)
我认为,在这种情况下,重要的是成员函数disp()
不会隐藏在D
类型的所有对象中。它是一个存在于一个地方的单一功能。是否有任何对象尝试呼叫disp()
是由代码决定的。
您的static_cast
将生成编译器认为指向D
的指针,而不管您传递的指针。一旦你有一个指向D
的指针,编译器就会让你试着调用disp()
。
换句话说,static_cast
无法保护您不会错误地投射指针。
答案 3 :(得分:1)
当您将指向分配为B
的对象的指针强制转换为指向派生类D
的指针时,您做了一件非常糟糕的事情。这是标准所说的,强调我的:
5.2.9静态演员
如果类型“指向cv1 B的指针”的rvalue指向实际上是D类型对象的子对象的B,则生成的指针指向类型D的封闭对象。否则,演员表的结果未定义。
您通过执行static_cast
调用了未定义的行为。编译器和运行时可以执行任何操作,并且在程序调用未定义的行为时仍然符合要求。
答案 4 :(得分:0)
您需要了解the difference between the various types of C++ casts。
你在这里使用静态演员,这与说“我真的不在乎它是否真的属于那种类型 - 只是尽力而为”。
在这种情况下,您想知道您的指针是否实际上是您要将其转换为的派生类型。你应该使用dynamic_cast。仅当指针的类型正确时,此强制转换才会成功。这意味着,如果失败,它将返回一个NULL指针。
你所看到的行为是当你没有为工作使用正确的演员时会发生什么,虽然你可以尝试解释它,但是你真的应该避免它,因为它属于未定义的行为领域。换句话说,您不能指望编译器甚至同一编译器的不同版本具有相同的副作用。换句话说,避免它。