c ++中的多态性,父类向量中的类型丢失

时间:2012-01-09 14:49:23

标签: c++ oop polymorphism

我有一个A类,它是B类和C类的父类。 还有一个X类,它是Y和Z的父级。

class A {};
class B : public A {};
class C : public A {};


class X
{
    void foo(A) { std:: cout << "A"; }
};

class Y : public X
{
    void foo(B) {std::cout << "B"; }
};

class Z : public X
{
    void foo(c) {std<<cout <<"C"; }
};

int main()
{
    B b;
    C c;

    Y y;
    Z z;

    y.foo(b);//prints B // b is a B, and Y::foo takes a B, hence print B
    y.foo(c);//prints A // mismatch between types, fall back and print A
    z.foo(b);//prints A // mismatch between types, fall back and print A
    z.foo(c);//prints C // c is a C, and Y::foo takes a C, hence print C

    std::vector<A> v;
    v.push_back(b);
    v.push_back(c);

    //In this loop, it always prints A, but *this is what I want to change* 
    for (size_t i = 0; i < v.size(); ++i)
    {
        z.foo(v.at(i));
        y.foo(v.at(i));
    }
}

是否可以让项目打印与硬编码调用相同的结果? 这意味着我会将它们视为原始类型,而不是其父类型? 或者,一旦我把它们放在A的矢量中,它们将永远属于A型?

2 个答案:

答案 0 :(得分:6)

您所看到的是 Object Slicing 您将Derived类的对象存储在一个向量中,该向量应该存储Base类的对象,这会导致对象切片,并且被存储的对象的派生类特定成员被切掉,因此存储在向量中的对象就像基类的对象。

解决方案:

您应该在向量中存储指向Base类对象的指针:

vector<X*> 

通过存储指向Base类的指针,不存在切片,您可以通过创建函数virtual来实现所需的多态行为。 正确的方法是使用合适的 Smart pointer ,而不是在向量中存储原始指针。这样可以确保您不必手动管理内存, RAII 会自动为您执行此操作。

答案 1 :(得分:1)

这称为切片。当您将push_back元素std::vector<A>转换为elements时,它基本上会将A复制到std::vector<A*>新构建的实例中。因此,来自派生类的对象的一部分将丢失(“切掉”)。

为了避免切片,你需要使用一个存储指针而不是元素的容器,所以你应该使用 std::shared_ptr ,或者如果你的元素是堆分配的,最好是一些向量一种smartpointer(C ++ 11中的std::unique_ptrboost::shared_ptr,否则为std::tr1::shared_ptrX

但是,即使您更改了代码,您的代码也无法正常工作: YZA*都按值显示参数,而向量中的所有元素都有A类型,因此取消引用它们会产生A& ,所以它仍然会调用错误的方法。这可以通过将签名更改为始终使用A*dynamic_cast并使用class X { void foo(A*) { std:: cout << "A"; } }; class Y : public X { void foo(A* p) { if ( dynamic_cast<B*>(p) ) std::cout << "B"; // requires virtual methods in A else X::foo(p); } }; class Z : public X { void foo(A*){ if ( dynamic_cast<C*>(p) ) std::cout << "C"; // requires virtual methods in A else X::foo(p); } }; 尝试将其转换为类型来解决:

dynamic_cast

当然A, B, C有点贵,但如果这是一个问题,你可能想重新考虑你的设计。此外,您需要确保virtual包含一些virtual destructor方法(无论如何,dynamic_cast都是个好主意),因为否则{{1}}将无效)