2012-03-31 115 views
0

我知道static_cast,dynamic_cast的存在。但我似乎无法找到一个具体的理由来说服自己,为什么要从基地派生或从基地派生,反之亦然?为什么我们需要从基类型转换为派生类型并返回?

代码中的任何示例将不胜感激。

UPDATE

class Base 
{ 
    public: 
     void foo(); 
    private: 
     int _x; 
}; 

class Derive: Base 
{ 

}; 

Base *b = new Derive; //will b behave the same as if it's a Derive *? 
Derive *d = new Base; //I think d can't behave like a Derive * but Base *, right? 
+2

对于一个考虑周全的设计,如果不是0,则使用类型转换的次数最少。 – 2012-03-31 12:19:53

+0

'b'将表现为'Derive *',因为如果您将一个虚拟成员函数添加到'Base'并覆盖它在'Derive'中后者会在你执行'b-> f()'时被调用。 'd'的定义会给你一个编译时错误。 – 2012-03-31 12:40:08

+0

@NicolaMusatti,为什么编译错误? – Alcott 2012-03-31 12:45:18

回答

2

实际上,这些演员阵容是代码中不确定事件的明显标记,因此在完美的世界中,您不应该使用它们。

但在某些情况下,他们是正确的工具。

对于的static_cast,基本上有两种情况:

1.原始转换。

当你确实需要一些整数在一个涉及浮点的演算中被处理。

float ratio = static_cast<float>(pixel_pos.x)/static_cast<float>(pixel_pos.y); // x and y are integers because pixel positions are absolute, but we need to get a floating point value here 

2.你有一些外部API的对象,你想要得到的特定的子类。

Thing* thing = factory.create("shoe"); // Even if I don't have it's real type, I know it's a shoe! 

Shoe* shoe = static_cast<Shoe*>(thing); // I need to use Shoe interface so lets cast it. 

如果你设计了系统,也许你可以做得更好,以避免演员。但是如果你没有,并且你使用的API提供了基本类型作为你使用它的一种方式,那么你没有别的选择,只能投射。

static_cast也很有用,因为它可以让你在编译时假设一些东西,所以你应该首先使用它,因为它需要你确定你在做什么。

3。你不知道什么是对象的真实类型。

但是,在运行时需要知道实际类型时有一种特殊情况,因为在另一次无法让您知道它。典型的情况是,当你从外部系统接收某种物体并没有对现实类型的对象

void on_something_happen(const Event& event) // callback triggered when an event occured in the library system this callback is plugged in 
{ 
    // here I want to manage two cases 
    ThingEvent* thing_event = dynamic_cast<ThingEvent*>(&event); 

    if(thing_event) 
    { 
     // do my thing 
    } 
    else 
    { 

     // ok this event HAVE TO be a FooEvent, otherwise this should crash 
     FooEvent& foo_event = dynamic_cast<FooEvent&>(event); 
     // do my thing 

    } 




} 
+0

因此,如果'dynamic_cast'失败,它将返回0.那么它什么时候会失败呢? – Alcott 2012-03-31 12:54:55

+0

如果在投射指针时失败,它将返回nullptr(或用C++ 03编译器为0),如果您投射引用,则会引发异常。您应该尝试更多地了解何时使用哪个演员阵容。也知道dynamic_cast在运行时有一定的成本,这对于知道时间和内存非常重要,因为它会添加RTTI。 – Klaim 2012-03-31 13:01:37

+0

实际上,它不返回'0'也不返回nullptr,而是返回正确指针类型的空指针(当转换为该指针类型时,相同的值为0或nullptr给出*)。 – celtschk 2012-03-31 13:11:05

1

假设你有:

struct A { 
    int i; 
}; 

struct B : A { 
    char c; 
}; 

struct C : A { 
    double d; 
}; 

而且有些功能f()返回一个指针A,对此你不知道的定义。 当你这样做:

A * a = f(); 

你怎么知道你可以用a做什么呢?根据上面的定义,每个BC也是A,所以你知道如果a不为空,你可以使用它的i数据成员没有问题。另一方面,要使用cd,您需要知道a的实际类型,并且这是通过dynamic_cast实现的。

让我们假设你知道,实际上是一个指向B.你可以做的是:

B * b = dynamic_cast<B *>(a); 
if (b != 0) 
    b->c = 'd'; 

(是的,我知道我们假设你知道,但这样的假设永远永远持有。 ..)

+0

如果'a'的实际类型不是'A *'而是'B *',那么我可以使用'a'就好像它是一个指向'B'对象的指针? – Alcott 2012-03-31 12:29:54

+0

你需要演员。我会更新我的答案以提供示例。 – 2012-03-31 12:32:06

+0

如果'a'不是指向'B'而是'C'的指针,那么'dynamic_cast'会失败? – Alcott 2012-03-31 12:44:58

1

的典型情况的任何其他信息是需要的操作添加到现有的数据键入,但不能直接添加。

假设你有这样的阶级结构:

struct Base { 
    virtual doSomething() = 0; 
}; 

struct Derived1 : Base { 
    virtual doSomething(); 
    int x,y; 
}; 

struct Derived2 : Base { 
    virtual doSomething(); 
    float a,b; 
}; 

现在你写传递一个基地&功能:

void f(Base& base); 

您希望能够打印有关的基本信息,但无论出于何种原因,您都不允许修改Base来添加此操作(例如,它是商业图书馆的一部分)。在这种情况下,您可能需要这样做:

void f(Base& base) 
{ 
    if (Derived1* p=dynamic_cast<Derived1*>(&base)) { 
    cout << "Derived1{" << p->x << "," << p->y << "}\n"; 
    } 
    else if (Derived2* p=dynamic_cast<Derived2*>(&base)) { 
    cout << "Derived2{" << p->a << "," << p->b << "}\n"; 
    } 
    else { 
    cout << "Unknown type\n"; 
    } 
} 

尽管这在面向对象的语言中通常被认为是不好的风格。一个问题是,如果您将新类添加到层次结构中,那么编译器无法帮助您找到需要添加代码以处理该新类实例操作的位置。

+0

我会去与一个门面/适配器/无论这被称为,而不是一个字符串if(dynamic_cast)else if(dynamic_cast)...。有些情况下可以进行动态演员表演,但**从不**可以演绎线性演员阵容。 – 2012-03-31 12:56:44

+0

因此,如果传入的Base&base实际上是对Derived1或Derived2对象的引用,那么'dynamic_cast'将成功,但如果'base'是对'Base'对象的引用,那么'dynamic_cast'将返回0? – Alcott 2012-03-31 12:58:24

+0

@Alcott:由于'Base'是抽象的,因此不能有任何'Base'对象不是从'Base'派生的动态类型。 – celtschk 2012-03-31 13:06:15

相关问题