Polymorphism c++ wrong output

时间:2016-07-11 18:54:10

标签: c++

Why output for this code is Class A I expected to see "Class AAA"

#include <iostream>    
using namespace std;

class A
{
    public:
    A(){}
    virtual int Method1(int a, int b){cout << "Class A" << endl; return a+b;    }
};

class AA:public A
{
    public:
    AA(){}
    int Method1(int a, int b){cout << "Class AA" << endl; return a*b;}
};

class AAA:public A
{
    public:
    AAA(){}
    int Method1(int a, int b){cout << "Class AAA" << endl; return a/b;}
};

class B
{
public:
   B(){}
   B(A a):obj(&a){}
   int Method2(int a, int b){ return obj->Method1(a,b);}

private:
    A *obj;
};

int main() {
    A *a = new AAA();
    B *b = new B(*a);
    b->Method2(2,1);
    return 0;
}

I have new AAA object passed to class B and assigned to *obj. What is wrong with this ? Best Regards,

1 个答案:

答案 0 :(得分:2)

You are slicing the object that is passed to the B() constructor, so all B sees is an instance of A and not an instance of AAA.

Polymorphism only works when you access a derived object by pointer/reference so the full vtable of virtual methods is accessible. That also includes when passing derived objects around. If you pass a derived class to a base class variable/parameter by value, you will slice the object, and the variable/parameter will only have access to the base class vtable entries.

So you need to change B accordingly, either like this:

class B
{
public:
   B() : obj(0) {} // <-- every constructor needs to initialize members!
   B(A *a) : obj(a) {} // <-- accept A by pointer
   int Method2(int a, int b) { return (obj) ? obj->Method1(a,b) : 0; }

private:
    A *obj;
};

int main() {
    A *a = new AAA();
    B *b = new B(a); // <-- AAA passed by A* pointer

    b->Method2(2,1);

    // don't forget these
    delete b;
    delete a;

    return 0;
}

Or this:

class B
{
public:
   B() : obj(0) {} // <-- every constructor needs to initialize members!
   B(A &a) : obj(&a) {} // <-- accept A by reference
   int Method2(int a, int b) { return (obj) ? obj->Method1(a,b) : 0; }

private:
    A *obj;
};

int main() {
    A *a = new AAA();
    B *b = new B(*a); // <-- AAA passed by A& reference

    b->Method2(2,1);

    // don't forget these
    delete b;
    delete a;

    return 0;
}

Or even this:

class B
{
public:
   // <-- note: no default constructor!
   B(A &a) : obj(a) {} // <-- accept A by reference
   int Method2(int a, int b) { return obj.Method1(a,b); }

private:
    A &obj;
};

int main() {
    A *a = new AAA();
    B *b = new B(*a); // <-- AAA passed by A& reference

    b->Method2(2,1);

    // don't forget these
    delete b;
    delete a;

    return 0;
}

In any case, note that A needs a virtual destructor so derived destructors can get called correctly when delete is called on a base class A* pointer or A& reference:

class A
{
public:
    ...
    virtual ~A() {} // <-- add this
    ...
};

If you are using C++11 or later, you should use std::unique_ptr and std::shared_ptr instead of raw pointers, let the compiler handle the (de)allocations for you:

#include <memory>

class B
{
public:
   B(std::shared_ptr<A> &a) : obj(a) {}
   int Method2(int a, int b) { return obj->Method1(a,b); }

private:
    std::shared_ptr<A> obj;
};

int main() {
    std::shared_ptr<A> a(new AAA);
    std::unique_ptr<B> b(new B(a));
    // or: if you are using C++14 or later:
    /*
    std::shared_ptr<A> a = std::make_shared<AAA>();
    std::unique_ptr<B> b = std::make_unique<B>(a);
    */

    b->Method2(2,1);

    return 0;
}