2016-07-11 5 views
0

このコードの出力はクラスAであるのはなぜ新しいAAAのオブジェクトクラスBに渡され、*のOBJに割り当てを持っている「クラスAAA」多型C++間違った出力

#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; 
} 

を参照することが期待。 これは何が問題ですか? よろしく、

+2

参照http://stackoverflow.com/questions/274626/what-is-object-slicing – sakra

+1

'B(A):OBJ(A){}' 'B(あるべきですA&a):obj(&a){} 'そうでなければ、オブジェクトをスライスします。 – AndyG

+0

Bコンストラクタを参照またはポインタでパラメータにする – bisthebis

答えて

2

あなたがそうBが見るすべてはAなくAAAのインスタンスのインスタンスである、B()コンストラクタに渡されslicing the objectです。

多態性は、仮想メソッドの完全なvtableにアクセスできるように、ポインタ/参照によって派生オブジェクトにアクセスする場合にのみ機能します。また、派生したオブジェクトを渡すときも含まれます。派生クラスを値で基本クラスの変数/パラメータに渡すと、オブジェクトがスライスされ、変数/パラメータは基本クラスのvtableエントリにのみアクセスできます。

ですから、どちらかのように、それに応じてBを変更する必要があります。

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; 
} 

またはこの:

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; 
} 

あるいはこの:いずれの場合で

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; 
} 

、注意してAは仮想デストラクタを必要とするので、デストラクタはdeleteは、基底クラスA*ポインタまたはA&参照に呼び出されます。

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

あなたがC++ 11以降を使用している場合は、コンパイラは(ド)を処理させ、代わりに生のポインタのstd::unique_ptrstd::shared_ptrを使用する必要がありますあなたのための割り当て:

#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; 
}