我对以下代码没有按预期工作感到有点惊讶:
#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;
}
那么,返回值时虚拟方法不起作用吗?我应该恢复在堆上分配返回类,还是有更好的方法?
(实际上我想这样做是为了让一些从容器类继承的不同类根据类返回不同的迭代器类实例...)
答案 0 :(得分:17)
多态行为不能按值运行,您需要返回指针或引用才能生效。
如果按值返回,则会得到所谓的“切片”,这意味着只返回对象的父部分,因此您已成功将子对象条带化为父对象,这根本不安全
答案 1 :(得分:0)
要获得动态重载,您需要使静态类型与动态类型不同。你不在这里。返回引用或指针(并注意实时)而不是值。
答案 2 :(得分:0)
虚拟适用于类型的指针或引用声明,因此更改代码:虚拟RetA& GetValue()或虚拟RetA * GetValue(), 但是你不是关于虚拟问题 - 你使用“复制语义”。
答案 3 :(得分:0)
你必须返回指针或地址
virtual RetA GetValue () { return RetB (); }
如果没有,你从GetValue获得的是一个RetA而不是RetB。
答案 4 :(得分:0)
除了已经提到的“切片”问题之外,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
}