Добавление члена класса строкового типа вызывает функцию базового класса вместо дочернего - PullRequest
3 голосов
/ 18 октября 2019

Почему следующий код выводит 0, но если вы закомментируете "std :: string my_string", он печатает 1?

#include <stdio.h>
#include <iostream>

class A {
  public:
    virtual int foo() {
      return 0;
    }
  private:
    std::string my_string;
};

class B : public A {
  public:
    int foo() {
      return 1;
    }
};

int main()
{
    A* a;
    if (true) {
      B b;
      a = &b;
    }
    std::cout << a->foo() << std::endl;
    return 0;
}

Я также понимаю, что замена std :: string на std: string *также заставляет код печатать 1, как и удаление оператора if, хотя я не понимаю, почему все это верно.

Halp, пожалуйста.

РЕДАКТИРОВАТЬ: Это, кажется,быть из-за висящего указателя. Тогда что является стандартным шаблоном в C ++, чтобы сделать что-то подобное в Java:

Animal animal; 
boolean isDog = false; 
// get user input to set isDog 
if (isDog) { 
  animal = new Dog();
} else {
  animal = new Cat();
}
animal.makeNoise(); // Should make a Dog/Cat noise depending on value of isDog.

1 Ответ

5 голосов
/ 18 октября 2019

Проблема

Программа имеет Неопределенное поведение . b находится только в области видимости тела if. Вы не можете рассчитывать на логические результаты при доступе к висячему указателю .

int main()
{
    A* a;
    if (true) {
      B b; // b is scoped by the body of the if.
      a = &b;
    } // b's dead, Jim.
    std::cout << a->foo() << std::endl; // a points to the dead b, an invalid object
    return 0;
}

TL; DR Solution

int main()
{
    std::unique_ptr<A> a; // All hail the smart pointer overlords!
    if (true) {
      a = std::make_unique<B>();
    }
    std::cout << a->foo() << std::endl;
    return 0;
} // a is destroyed here and takes the B with it. 

Объяснение

Youможет указывать a на объект с динамическим временем жизни

int main()
{
    A* a;
    if (true) {
      a = new B; // dynamic allocation 
    } // b's dead, Jim.
    std::cout << a->foo() << std::endl; 
    delete a; // DaANGER! DANGER!
    return 0;
}

К сожалению, delete a; также является неопределенным поведением, потому что A имеет деструктор не virtual. Без виртуального деструктора объект, на который указывает a, будет уничтожен как A, а не как B.

. Исправление для этого - дать A виртуальный деструктор, чтобы позволить емууничтожить правильный экземпляр.

class A {
  public:
    virtual ~A() = default;
    virtual int foo() {
      return 0;
    }
  private:
    std::string my_string;
};

Нет необходимости изменять B, поскольку после объявления функции virtual она остается virtual для своих дочерних элементов. Следите за final.

Но лучше всего избегать необработанных динамических выделений , поэтому мы можем сделать еще одно улучшение: ИспользованиеУмные указатели .

И это возвращает нас к решению.

Документация для std::unique_ptr

Документация для std::make_unique

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