0

C++ で問題が発生しています。派生クラスを取得し、それを他の基本クラスと共に基本クラスとして保持したいと考えています。次に、基本クラスであることのみを必要とするすべてのクラスに対していくつかの操作を実行します。この後、クラスの派生部分を元に戻したいと思います。

問題を可能な限り単純化し、以下に示すテスト プログラムを作成しようとしました。

#include <vector> //Needed for the holding class and main
#include <iostream> //For output to terminal

class Base
{
    int a; //Variable for all classes derived from base

public: 
    Base() { a = 13; }; //Set a to something

    int get_a() { return a; }; //Access a
    virtual void bugger_all() {}; //To make the class polymorphic (my first alarm that I might be doing something wrong with polymorphism
};


class Derived:public Base
{
    int b;//not accessable by the base class
public: 
    Derived():Base() { b = 7; };//Set b and a to something
    int get_b() { return b; };

    void bugger_all() {}; //Implements to virtual function from the base class (just to make everything polymorphic)
};


//Holds several bases
class Holder
{
    std::vector<Base> bases;//Holds a vector of base classes, not nessesarily derived classes but can be

public:
    void add(Base to_add) { bases.push_back(to_add); };//Add something to the vector
    Base get(int i) { return bases[i]; };//Get a certain vector
    void print_all() { for(unsigned int i=0; i<bases.size(); i++) std::cout << bases[i].get_a() << "\n"; }; //Prints a for all bases, note that this is specific only to bases and can also be used for derived classes
    std::vector<Base> get_all() { return bases; };//Returns the main vector
};


int main(int argc, char *argv[])
{
    Derived higher = Derived(); //The derived class (extends the base class)
    Base lower = Base(); //Simply a base class, for comparisons
    Holder holding_class = Holder();//Will hold both the above objects

    //Add the objects to the holder
    holding_class.add(lower);
    holding_class.add(higher);

    //Prints everything in the holder
    holding_class.print_all();

    std::vector<Base> all_bases = holding_class.get_all();  //Get all the bases back again
    std::cout << all_bases[1].get_a() << "\n"; //Test to see if we have retained a from the derived class

    Derived higher_again = *(static_cast<Derived*>(&all_bases[1])); //Cast is done here, take a base class and change it to a derived class

    std::cout << higher_again.get_b() << "\n"; //Output something specific to the derived class

    return 0;//Exit
}

g++ を使用してコンパイルされているため、エラーは発生しません。プログラムが実行され、出力が

13
13
13
0

プログラムが意図したとおりに機能した場合、出力は

13
13
13
7

これは、「higher_again」が正しくキャストされておらず、その「b」値が何らかの形で失われ、コンパイラーが単に値を 0 に設定したことを示しています。

周りを見回すと、dynamic_cast と static_cast の使用はお勧めできません (おそらくこのような問題のため)。ただし、この問題を回避する方法はありません。また、ポリモーフィズム (役に立たない仮想関数を作成する必要がある) に関して、おそらく何か間違ったことをしていることにも気付きました。どんなアドバイスも役に立ちます。前もって感謝します。

4

3 に答える 3

3

これは、「higher_again」が正しくキャストされておらず、その「b」値が何らかの形で失われ、コンパイラーが単に値を 0 に設定したことを示しています。

Baseタイプをベクターに格納しているためslicingDerived objects「ベース」オブジェクトにスライスされます。

代わりにポインタをベースに格納してみてください。派生オブジェクトはスライスされず、ポリモーフィズムはポインタを介して機能します。

 std::vector<std::shared_ptr<Base>> bases;
于 2013-01-18T03:42:59.287 に答える
3

値型参照型を区別する必要があります。

値の種類の例:

Base base;
Derived derived;

参照型の例

Derived* p_derived = &derived;
Base& ref_base = *p_derived;

値 typeを宣言すると、正確に指定した型のオブジェクトにメモリが割り当てられるため、宣言したとおりになります。

参照型を宣言すると、オブジェクトにメモリが割り当てられなくなります。むしろ、既存のオブジェクトへのハンドルを持っています。参照型を使用すると、ハンドルによって参照されるポリモーフィズムで遊ぶことができます。

私は、値の型を、具体的なインスタンスを宣言する際に使用される厳密な変数と考えるのが好きです。それは、まさにあなたが宣言するということです。

対照的に、具体的なものから抽象化したい場合は、参照型ルーズ ハンドルとして使用するのが好きです。参照型の背後にある実際の値は、継承階層の下の参照型から何でもかまいません。したがって、Base* がある場合、実際に指されるオブジェクトは、Base または Base から派生したもの (Derived を含む) である可能性があります。

それでは、問題に戻ります。定義により、すべての std コンテナーは値ベースで機能します。ポリモーフィズムが必要な場合、このコンテキストでは、トリックは Base へのポインターを値として格納することです。たとえば、、、std::vector<Base*>などstd::unique_ptr<Base>...std::vector<Base>それ自体ではありません。

を保存する場合std::vector<Base>、ベクトルには Base インスタンスのみを含めることができ、他には何も持てません。したがって、Derived のインスタンスを保存しようとすると、Base である Derived インスタンスのコピーを保存することで、Derived にあるすべてのメモリを削除することになるため、これはスライシングと呼ばれます。なんで?では、 std コンテナは値ベースstd::vector<Base>であるため、オブジェクトを格納するのに十分なスペースしかありません。Base

OTOH、std::vector<Base*>保存する場合、保存される値は単なるポインターです。Base* のサイズは Derived* と同じです。これらの値を保存しても、問題やスライスはありません。追加のボーナスとして、より緩いハンドルを使用しているため、コンパイラーは vtables を使用して、本当に必要な正しいポリモーフィック呼び出しを検索できます。

したがって、ポリモーフィズムが必要な場合は、参照型を使用してください。

于 2013-01-18T03:55:04.040 に答える
2

ポリモーフィズムを意図したとおりに使用するには、動的に割り当てられたメモリに Base* を格納する必要があります。あなたが持っているとき...

std::vector<Base> bases;//Holds a vector of base classes, not nessesarily derived classes but can be

...ベクトルを作成しています。これは実質的に Base のパック配列でありb、派生クラスによって追加されたデータ メンバーのための物理的なスペースがありません。

本を探そう!;-P

于 2013-01-18T03:41:52.173 に答える