Даункаст с базы до деривации - PullRequest
0 голосов
/ 23 мая 2019

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

Я ожидаю от "A", но он дает мне от D даже после сотворения


class A
{
  public:
  virtual ~A(){} 
  virtual void print()
  {
    std::cout << "from A"<<std::endl;
  }
  };
class B:  virtual public A
{
  public:
    void print()
  {
    std::cout << "from B"<<std::endl;
  }
};       
class C: virtual public A
{
  public:
    void print()
  {
    std::cout << "from C"<<std::endl;
  }
};      
class D: public B, public C
{
  public:
    void print()
  {
    std::cout << "from D"<<std::endl;
  }
};


int main(){
    A* b = new B;
    A* c = new C;
    A* d = new D;
    A* a = new A;

    B* down = dynamic_cast<B*>(a);  // this gives me problem
    down->print();

    A* up= dynamic_cast<A*>(d); // upcasten dont print from A either
    up->print();

Ответы [ 2 ]

2 голосов
/ 23 мая 2019

При использовании dynamic_cast для преобразования указателя вам нужно проверить полученный указатель, чтобы убедиться, что приведение было допустимым, прежде чем получить к нему доступ. Доступ к неверному указателю приводит к неопределенному поведению.

    B* down = dynamic_cast<B*>(a);
    if (down == 0) {
        std::cerr << "a is not a B!\n";
    } else {
        down->print();
    }

Вам не нужно использовать dynamic_cast для "заброса". Вы можете назначить производное для родителя. Если отношения не существуют, вы получите ошибку во время компиляции.

    A* up= d;
    up->print();

Поскольку print является виртуальным методом, up->print() разрешает реализацию производного. Однако, если вы хотите увидеть реализацию базы, вы можете вызвать ее явно.

    d->A::print();

Попробуйте онлайн!

1 голос
/ 23 мая 2019

Возможность кастовать с базы на производную и с базы на производную - не единственная отличительная черта dynamic_cast.Ключевой особенностью является то, что dynamic_cast выполняет такое преобразование во время выполнения , после проверки того, что наиболее производный объект действительно соответствует (или фактически содержит ) желаемого целевого типа.,Это означает, что ошибка, если преобразование было неудачным, также является ошибкой во время выполнения, и что ваша программа должна быть подготовлена ​​к обработке таких ошибок во время выполнения.

Какие это могут быть ошибки?dynamic_cast может сообщить об ошибке либо путем возврата нулевого указателя, если вы выполняли приведение между типами указателей, скажем, от X* до Y*, либо путем выдачи исключения std::bad_cast, если вы выполняли приведение между ссылочными типами, например X&Y&.

Приведение из a со статическим типом A* к B* может привести к действительному указателю на B или к нулю.Последнее именно то, что произошло в вашем примере: a фактически указывает на законченный объект типа A, и в нем нет подобъекта типа B.Таким образом, приведение возвращает нулевой указатель, который затем сразу используется для вызова функции-члена down->print().Любая попытка разыменования значения нулевого указателя - это UB (неопределенное поведение), которое вполне может проявиться как segfault.

Защита вашей программы от такого segfault проста: просто убедитесь, что возвращаемое значение не равно null:

B* down = dynamic_cast<B*>(a);
if (down)
{
    // Use down all you want
}
else
{
    // Report an error, skip some actions
    // or return from the function.
    // But don't use `down`!
}

Что касается печати "из D", то это потому, что метод print объявлен виртуальным.Это означает, что реализация из самого производного класса будет вызвана, даже если вы вызываете ее через указатель на базовый подобъект.

...