6

ここに私の問題があります: 基本クラスから継承するクラスで呼び出したい .h ファイルで定義された仮想メソッドがあります。残念ながら、派生クラスのメソッドは呼び出されません。私がやろうとしていることを実装するためのより良い方法はありますか?

#ifndef ofxBASE_SND_OBJ
#define ofxBASE_SND_OBJ

#include "ofConstants.h"

class ofxBaseSndObj {

public:

    virtual string getType(){}

    string key;

};

#endif

これが私の話題のクラスです

#ifndef OFXSO_BUZZ
#define OFXSO_BUZZ

#include "ofxBaseSndObj.h"

class ofxSOBuzz : public ofxBaseSndObj
{
public:
    string getType();
};

#endif

ofxSOBuzz.cpp

string ofxSOBuzz::getType()
{
    string s = string("ofxSOBuzz");
    printf(" ********* returning string type %s", s.c_str()); // doesn't get called!
    return s;
}

次に、別のクラスで次のように呼び出します。

string ofxSndObj::createFilter(ofxBaseSndObj obj)
{
    string str = obj.getType();
    if(str.compare("ofxSOBuzz") == 0)
    {
        printf(" all is well ");
    }
}

上記のメソッドでは、すべてが ofxBaseSndObj オブジェクトを拡張する多くの種類のオブジェクトの 1 つを渡すことができる必要があります。提案や指針をいただければ幸いです。ありがとう!

4

6 に答える 6

25

この行を変更します。

string ofxSndObj::createFilter(ofxBaseSndObj obj)

string ofxSndObj::createFilter(ofxBaseSndObj& obj)

あなたがやっていることは、値渡しです(コピーを渡します)。

これは、オブジェクトを関数にコピーしていることを意味します。関数は実際に渡す型がわからないため、関数宣言で定義された型のみを渡し、基底クラスのコピーを作成します (これはスライシングの問題として知られています)。

解決策は参照渡しです。

関数でオブジェクトを変更したくない場合 (おそらく、値渡しを行っていたため、元のオブジェクトを変更できませんでした)、const 参照を渡します。

class ofxBaseSndObj
{
    public:
        virtual string getType()  const;
        // If the method does not change the object mark it const

        string key;

};

string ofxSndObj::createFilter(ofxBaseSndObj const& obj)
{
    // allowed to call this if getType() is a const
    string str = obj.getType();

    if(str.compare("ofxSOBuzz") == 0)
    {
        printf(" all is well ");
    }
}
于 2008-10-25T21:25:22.370 に答える
10

インスタンスをオブジェクトへのポインター(または参照)として createFilter に渡す必要があります。値で渡しているため、コンパイラは、引数として使用する派生オブジェクトを基本クラスのインスタンスにコピーします。これを行うと、元は派生型であったという事実が失われます。

書かれているように、 ofxBaseSndObj::getType の宣言は何も返さないため、コードは実際にはコンパイルされません。これが抽象メソッドであること、または空の文字列を返すことを意味していましたか?

それを抽象メソッドにした場合、コンパイラは ofxSndObj::createFilter メソッドで抽象クラスをインスタンス化しようとしていると文句を言うでしょう。

于 2008-10-25T21:24:11.967 に答える
2

この問題は、C++ では「スライス」と呼ばれます。

于 2008-10-25T21:27:47.810 に答える
2

コピー コンストラクターと operator= private を作成することは、このバグの再発を防ぐ効果的な方法です。

例えば:

class ofxBaseSndObj {
public:
    virtual string getType(){}
    string key;

private:
    ofxBaseSndObj(const ofxBaseSndObj& rhs);
    ofxBaseSndObj& operator=(const ofxBaseSndObj& rhs);
};

他に正当な理由がない場合は、C++ の組み込み RTTI を使用する必要があります。その後、typeid 演算子を使用できます。デフォルトでオンになっていない場合は、コンパイラのドキュメントを参照してオンにしてください。

于 2008-10-25T21:40:20.077 に答える
1

スライスの問題に対処した人もいます。次に、わかりました、つまり、基本型を判別するために何かをする必要があることはわかっていますが、継承されたオブジェクトの種類を判別するために列挙型ルックアップを実行するよりも洗練された方法はありますか?

オブジェクトのタイプのクエリと切り替えは、OO アプローチの要点を逃した貧弱な設計です。

それ以外の

string ofxSndObj::createFilter(ofxBaseSndObj& obj)
{
    string str = obj.getType();
    if(str.compare("ofxSOBuzz") == 0)
    {
        // do ofxSOBuzz - specific thing
    }
    else if(str.compare("some other derived class") == 0)
    {
        // do stuff for other derived classes
    }
       // etc...
}

興味深い動作を仮想関数にします。

class ofxBaseSndObj {

public:
    // get rid of getType()
    virtual void HelpCreateFilter() = 0;
};


string ofxSndObj::createFilter(ofxBaseSndObj& obj)
{
    // Let the derived class do it's own specialized work.
    // This function doesn't need to know what it is.
    obj.HelpCreateFilter();
    // rest of filter creation
}

これが元のバージョンよりも優れているのはなぜですか?ofxSndObj::createFilterofxBaseSndObj の将来の派生クラスがシステムに追加された場合、変更する必要がないためです。バージョンは、新しい派生クラスごとに拡張する必要があります。これが不明な場合は、もう少しコードを投稿してみてください。コードやクラス名からは、これらの関数が何をすべきかわかりません。

于 2008-10-25T22:39:10.690 に答える
-1

dynamic_cast または type_id を使用できます

于 2008-10-25T21:41:22.790 に答える