зачем нам приводить от базового типа к производному типу и обратно? - PullRequest
0 голосов
/ 31 марта 2012

Я знаю о существовании 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?

Ответы [ 3 ]

2 голосов
/ 31 марта 2012

На самом деле эти броски являются очевидными признаками чего-то необычного в коде, поэтому в идеальном мире их не следует использовать.

Но в некоторых случаях они являются подходящим инструментом для работы.

Для static_cast существует в основном 2 случая:

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 также полезнопотому что это позволяет вам предполагать что-то во время компиляции, поэтому вы должны использовать это в первую очередь, потому что это требует от вас быть уверенным в том, что вы делаете.объекта.

Однако существует особый случай, когда вам нужно знать реальный тип во время выполнения, потому что вы не можете узнать его в другое время.Типичный случай, когда вы получаете какие-то объекты из внешней системы, и нет никакой другой информации о реальном типе объекта

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 голос
/ 31 марта 2012

Типичная ситуация - это необходимость добавить операцию к существующему типу данных, но вы не можете добавить ее напрямую.

Предположим, у вас есть такая структура класса:

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

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

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

Теперь вы пишете функцию, которой передается Base &:

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

Хотя это обычно считается плохим стилем в объектно-ориентированном языке.Одна из проблем заключается в том, что если вы добавите новый класс в вашу иерархию, то компилятор не поможет вам найти места, где вам нужно добавить код для обработки операций над экземплярами этого нового класса.

1 голос
/ 31 марта 2012

предположим, у вас есть:

struct A {
  int i;
};

struct B : A {
  char c;
};

struct C : A {
  double d;
};

И некоторая функция f(), возвращающая указатель на A, для которого вы не знаете определения. Когда вы делаете:

A * a = f();

Как вы знаете, что вы можете сделать с a? Согласно приведенному выше определению каждый B и C также является A, так что вы знаете, что если a не равно нулю, вы можете без проблем использовать его элемент данных i. С другой стороны, чтобы использовать c или d, вам нужно знать фактический тип a, и это достигается с помощью dynamic_cast.

Предположим, вы знаете, что на самом деле указатель на B. Что вы можете сделать:

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

(Да, я знаю, что мы предполагали, что вы знаете , но такие предположения никогда не верны ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...