为什么我们需要从基类型转换为派生类型并返回?

时间:2012-03-31 12:16:18

标签: c++ casting

我知道static_castdynamic_cast的存在。但我似乎无法找到一个具体的理由来说服自己为什么从基地推导出来,反之亦然?

代码中的任何示例都将受到赞赏。

更新

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?

3 个答案:

答案 0 :(得分: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

    }




}

答案 1 :(得分:1)

假设你有:

struct A {
  int i;
};

struct B : A {
  char c;
};

struct C : A {
  double d;
};

并且某些函数f()返回指向A的指针,您不知道该定义。 当你这样做时:

A * a = f();

您如何知道a可以做些什么?根据上面的定义,每个BC也是A,因此您知道如果a不为null,您可以使用其i数据成员没有问题。另一方面,要使用cd,您需要知道a的实际类型,并使用dynamic_cast来实现。

让我们假设您知道a实际上是指向B的指针。您可以做的是:

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

(是的,我知道我们假设你知道它,但这样的假设永远不会永远存在......)

答案 2 :(得分:1)

典型情况是需要向现有数据类型添加操作,但不能直接添加。

假设你有这个类结构:

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

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

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

现在您正在编写一个传递Base&amp;:

的函数
void f(Base& 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";
  }
}

但这通常被认为是面向对象语言中的错误样式。一个问题是,如果向层次结构中添加新类,则编译器将无法帮助您找到需要添加代码以处理该新类实例上的操作的位置。