问题描述
从父级到子级的 dynamic_cast 在实践中何时有用? 这总是不好的做法吗?
这不是以下内容的重复: C++ Polymorphism: from parent class to child Run-time type information in C++
#include<iostream>
using namespace std;
class B { public: virtual void fun() {cout<<"!class B!\n";} };
class D: public B { public: void fun() {cout<<"!class D!\n";} };
int main()
{
///////// 1. Dynamic initialization parent from child
B *bAux = new D;
D *d = dynamic_cast<D*>(bAux);
cout<<"1.1. for d is "; d->fun(); //class D
B *b = dynamic_cast<B*>(d);
cout<<"1.2. for b is "; b->fun(); //class D
///////// 2. Dynamic initialization child from parent
//D *dAux4 = new B; //invalid conversion from ‘B*’ to ‘D*’
///////// 3. Casting the parent to child
B *bAux2 = new B;
cout<<"3.1. for bAux2 is "; bAux2->fun(); //class B
D *d2 = dynamic_cast<D*>(bAux2);
if (d2 != NULL){ cout<<"3.2. for d2 is "; d2->fun(); } //cannot cast parent to child
else cout<<"3.2. cannot cast B* to D* \n";
///////// 4. Casting the child to parent
D *dAux3 = new D;
cout<<"4.1. for dAux3 is "; dAux3->fun(); //class D
B *b3 = dynamic_cast<B*>(dAux3);
cout<<"4.2. for b3 is "; b3->fun(); //class D
getchar();
return 0;
}
其次,让我们得出结论。 输出为:
1.1. for d is !class D!
1.2. for b is !class D!
3.1. for bAux2 is !class B!
3.2. cannot cast B* to D*
4.1. for dAux3 is !class D!
4.2. for b3 is !class D!
但是对于情况 3,我们实际上尝试从 B* 动态转换为 D*(如上所述,这是不可能的)。我想知道这在哪些情况下有用?您可以打破强制类型转换(类似地,在静态类型检查的情况下,有一个众所周知的问题:既然熊是动物,那么熊的集合不应该是动物的集合吗?不,因为然后你就可以在集合中插入一只狼,并在熊中拥有一只狼)。
那么,问题是,在哪种情况下,从父级到子级的动态转换有用?这是一种不好的做法吗?
编辑: 在讨论之后,一个特例:
///////// 5. Try to cast the parent initialized from a child to another child
B *bAux = new D;
F *f = dynamic_cast<F*>(bAux);
if(f == NULL)
cout<<"5.1. Cannot cast the parent initialized from a child to another child \n";// f->fun(); //segmentation fault
B *b = dynamic_cast<B*>(f);
if(b == NULL)
cout<<"5.2. Cannot cast the NULL pointer back to parent \n"; //b->fun(); //segmentation fault
解决方法
dynamic_cast 对于你想要获取派生对象接口的情况很有用: 如果派生中存在 f() 而基数中没有 f() 函数,则您不能通过 B* 调用 f()。但 您可以将 dynamic_cast 转换为 D* 并调用 f() (如果 B* 指向 D 对象)。 (记住 dynamic_cast 是针对多态类型的) 考虑以下代码:
class B {
public:
virtual void g()
{
std::cout << "Base::g()\n";
}
};
class D : public B
{
public:
void f()
{
std::cout << "f()\n";
}
void g()
{
std::cout << "Derived::g()\n";
}
};
int main()
{
B* p = new D;
//p->f(); // class B has no member f
D* pd = dynamic_cast<D*>(p);
pd->f(); // ok f() will be called
return 0;
}
,
一个例子是当有多个相同类型的基类时。只有 dynamic_cast
可以从非唯一基类向下转换为最派生的:
struct A { virtual ~A() = 0; };
struct B1 : A {};
struct B2 : A {};
struct C : B1,B2 {};
C& f(A& a) { return dynamic_cast<C&>(a); } // Only dynamic_cast can cast A& to C&.
另一个例子是只有 dynamic_cast<void*>
可以返回一个指向完整对象地址的指针,其他类型转换都做不到。