Можно ли использовать виртуальные функции в возвращаемых значениях? - PullRequest
1 голос
/ 10 августа 2009

Я был немного удивлен, что следующий код не работал должным образом:

#include "stdio.h"

class RetA
{
public:
    virtual void PrintMe () { printf ("Return class A\n"); }
};

class A
{
public:
    virtual RetA GetValue () { return RetA (); }
};

class RetB : public RetA
{
public:
    virtual void PrintMe () { printf ("Return class B\n"); }
};

class B : public A
{
public:
    virtual RetA GetValue () { return RetB (); }
};

int main (int argc, char *argv[])
{
    A instance_A;
    B instance_B;
    RetA ret;

    printf ("Test instance A: ");
    ret = instance_A.GetValue ();
    ret.PrintMe (); // Expected result: "Return class A"

    printf ("Test instance B: ");
    ret = instance_B.GetValue ();
    ret.PrintMe (); // Expected result: "Return class B"

    return 0;
}

Итак, виртуальные методы не работают при возврате значения? Должен ли я вернуться к выделению возвращаемого класса в куче, или есть лучший способ?

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

Ответы [ 5 ]

17 голосов
/ 10 августа 2009

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

Если вы возвращаете по значению, вы получаете то, что известно как «нарезка», что означает, что возвращается только родительская часть объекта, поэтому вы успешно преобразовали дочерний объект в родительский объект, это вовсе не безопасно. .

Взгляните на: Что такое нарезка объектов?

0 голосов
/ 10 августа 2009

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

struct B 
{
   void print1() const { std::cout << "B" << std::endl; }
   virtual void print2() const { print1(); }
};
struct D : public B
{
   void print1() const { std::cout << "D" << std::endl; }
   virtual void print2() const { print1(); }
};
B& f() 
{
   static D d;
   return d; // the object returned is in fact a D, not a B
}
int main(){
   f().print1(); // B: calling a non-virtual method from a B reference
   f().print2(); // D: the method is virtual will exec B::print2
}
0 голосов
/ 10 августа 2009

вы должны вернуть указатель или адрес

virtual RetA GetValue () { return RetB (); }

Если нет, то вы получаете от GetValue RetA, а не RetB.

0 голосов
/ 10 августа 2009

Virtual применимо к указателю или ссылочному объявлению типа, поэтому измените свой код: виртуальный RetA & GetValue () или виртуальный RetA * GetValue (), Но вы говорите не о виртуальном - вы используете «копировать семантику».

0 голосов
/ 10 августа 2009

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

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