Когда я вызываю функцию в дочернем классе, она вместо этого вызывает функцию родительского класса - PullRequest
2 голосов
/ 25 апреля 2019

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

#include <iostream>

class Parent {
public:
    Parent(){}
    void print() {
        std::cout << "I'm the parent!" << std::endl;
    }
};

class ChildOne : public Parent {
public:
    ChildOne(){}
    void print() {
        std::cout << "I'm childOne!" << std::endl;
    }
};

class ChildTwo : public Parent {
public:
    ChildTwo() {}
    void print() {
        std::cout << "I'm childTwo!" << std::endl;
    }
};


int main(int argc, char const *argv[]) {
    Parent arr[] = {ChildOne(), ChildTwo()};
    int n = 2;

    for(int i = 0; i < n; i++) {
        arr[i].print();
    }

    return 0;
}

Вывод, который я получаю,

I'm the parent!
I'm the parent!

Где я хочу получить вывод

I'm childOne!
I'm childTwo!

Ответы [ 4 ]

3 голосов
/ 25 апреля 2019

Вы неявно приводите все свои экземпляры к типу Parent, когда сохраняете их в этом типизированном массиве.Так как вы используете статическую диспетчеризацию, вы всегда будете вызывать метод для базового типа, который вы (неявно) понизили до.

2 голосов
/ 25 апреля 2019

Во-первых, функция-член Parent должна быть virtual.

class Parent
{
   public:
       Parent(){}
       virtual void print() {std::cout << "I'm the parent!" << std::endl;};
};

Тогда дочерние элементы должны переопределить ее.В C ++ 11 и более поздних версиях рекомендуется использовать override

class ChildOne : public Parent
{
   public:
      ChildOne(){}
      void print() override {std::cout << "I'm childOne!" << std::endl;};
};

Второе, на что следует обратить внимание, это то, что ваш код в main()

Parent arr[] = {ChildOne(), ChildTwo()};

инициализирует два Parent объекта путем нарезки, т. Е. arr[0] и arr[1] оба имеют тип Parent, а не ChildOne или ChildTwo соответственно.

Для адресации это необходимочто arr будет массивом указателей и соответственно инициализирован.

 Parent *arr[] = {new ChildOne(), new ChildTwo()};

, за которым может следовать

for(int i = 0; i < 2; i++)
{
    arr[i]->print();
}

for(int i = 0; i < 2; i++)    // to avoid a memory leak
{
    delete arr[i];
}
//  don't use elements of `arr` here

Лучший способ (C ++ 11 и более поздние версии)писать main() означает использовать std::unique_ptr из стандартного заголовка <memory>)

std::unique_ptr<Parent> arr[] = {new ChildOne(), new ChildTwo()};

, что позволяет покончить с циклом для освобождения элементов arr.В C ++ 14 и более поздних версиях объекты в arr могут быть созданы с использованием std::make_unique().

2 голосов
/ 25 апреля 2019

Ваш код страдает от двух проблем:

  1. вы вообще не переопределяете print(), потому что это не virtual.Таким образом, вы просто перегружаете в каждом дочернем классе, скрываете родительский метод.

  2. вы нарезаете свои объекты при сохранении их в вашем массиве.Таким образом, не имеет значения, является ли print() виртуальным, поскольку в массиве нет дочерних объектов, только родительские объекты.Полиморфизм работает правильно только при использовании указателей / ссылок на базовые классы.

Попробуйте вместо этого:

#include <iostream>

class Parent {
public:
    Parent(){}
    virtual void print() {
        std::cout << "I'm the parent!" << std::endl;
    }
};

class ChildOne : public Parent {
public:
    ChildOne(){}
    void print() override {
        std::cout << "I'm childOne!" << std::endl;
    }
};

class ChildTwo : public Parent {
public:
    ChildTwo() {}
    void print() override {
        std::cout << "I'm childTwo!" << std::endl;
    }
};

int main() {
    ChildOne c1;
    ChildTwo c2;
    Parent* arr[] = {&c1, &c2};

    int n = 2;
    for(int i = 0; i < n; i++) {
        arr[i]->print();
    }

    return 0;
}
1 голос
/ 25 апреля 2019

Вам необходимо указать родительскую функцию как virtual, а дочерние функции должны использовать ключевое слово override.

class Parent {
public:
    Parent(){}
    virtual void print() {
        std::cout << "I'm the parent!" << std::endl;
    }
};

class ChildOne : public Parent {
public:
    ChildOne(){}
    void print() override {
        std::cout << "I'm childOne!" << std::endl;
    }
};

class ChildTwo : public Parent {
public:
    ChildTwo() {}
    void print() override {
        std::cout << "I'm childTwo!" << std::endl;
    }
};
...