Как сделать так, чтобы метод child вызывался: виртуальное ключевое слово не работает?

Вот мой код,

#include<iostream>
#include<string>

using namespace std;

class TestClass
{
  public:
    virtual void test(string st1, string st2);

};

class ExtendedTest: public TestClass
{
  public:
    virtual void test(string st1, string st2);
};

void TestClass::test(string st1, string st2="st2")
{
     cout << st1 << endl;
     cout << st2 << endl;
}

void ExtendedTest::test(string st1, string st2="st2")
{
     cout << "Extended: " << st1 << endl;
     cout << "Extended: " << st2 << endl;
}

void pass(TestClass t)
{
    t.test("abc","def");
}


int main()
{
   ExtendedTest et;
   pass(et);
   return 0;
}

Когда я запускаю код, вызывается метод ("тест") базового класса. Но я ожидаю, что метод child вызывается, потому что я указал методы как виртуальную функцию.

Тогда как я могу заставить метод дочернего класса быть вызванным? Спасибо.

3 ответа

Решение
void pass(TestClass t)
{
    t.test("abc","def");
}

Когда вы делаете это, объект, который вы передаете, нарезается в TestClass и его личность потеряна, так что теперь он ведет себя как TestClass и вызывает метод соответственно.

Чтобы исправить это, вы хотите пройти t по ссылке, как предложено @Nick, или (не рекомендуется) по указателю. Теперь он сохранит свою идентичность и вызовет соответствующую функцию, пока test помечен виртуальным

Редактировать: исправлено сращено -> нарезано.. слишком много биошока..

Вам нужно изменить параметр для ссылки (или указатель)

void pass(TestClass &t)

Таким образом, оригинальный объект будет использоваться.

Как отмечалось выше, это результат "нарезки". Конкретные детали того, что происходит, заключаются в следующем:

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

Для вашего примера, конструктор копирования имеет следующую подпись:

TestClass::TestClass(const TestClass&);

Итак, что действительно происходит, это что-то вроде следующего (опять же, для вашего примера):

ExtendedTest et();
pass(et);
{ // entering scope of pass function ...
  TestClass t = TestClass(t_orig); // inserted by the compiler
  // evaluation of pass function ...
  // ...
} // leaving scope of pass function, t is destroyed.

Очевидно, что, поскольку переменная t создается как TestClass, любые вызовы функций-членов будут происходить из TestClass (а не ExtendedTest).

В заключение, вы должны всегда объявлять виртуальные деструкторы при использовании наследования. Это позволит избежать нарезки, когда объекты уничтожены.

Другие вопросы по тегам