1

同様の方法で何度も遭遇する問題があります。

例えば:

たとえば、データコンテナとして機能する一連の具象クラスのインターフェースとして機能する抽象基本クラスがあります。

class DataInterface
{
public:
    DataInterface();
    ~DataInterface();

    virtual void FetchData(void) = 0;
    virtual void ProcessData(void) = 0;
    virtual void ClearData(void) = 0;
}

具体的なクラスは次のようになります。

class BinaryData: public DataInterface
{
public:
    BinaryData();
    ~ BinaryData();

    virtual void FetchData(void);
    virtual void ProcessData(void);
    virtual void ClearData(void);

private:
    bool m_boolData;
}

class IntegerData: public DataInterface
{
public:
    IntegerData();
    ~ IntegerData();

    virtual void FetchData(void);
    virtual void ProcessData(void);
    virtual void ClearData(void);

private:
    int m_intData;
}

サブクラスは、DataInterface から継承したインターフェイスを実装します。ただし、データを保持するための属性が異なります。ここまでは順調ですね。

次のようにメイン関数でクラスを使用できます。

int main()
{
    int IntegerData;
    bool BoolData;
    DataInterface *pData1 = new BinaryData();
    DataInterface *pData2 = new IntegerData();  

    pData1->FetchData();
    pData2->FetchData();

    pData1->ProcessData();
    pData2->ProcessData();

    // now I want to get the data of pData1 and pData2, for example to write it into a file, show in visualization, ...
    IntegerData = pData2->GetData() ????
    BoolData = pData1->GetData() ????
}

問題は次のとおりです。

具体的なクラスからデータを取得するにはどうすればよいですか? 基本クラスのポインターしかないので、DataInterface で抽象ゲッター メソッドを定義する必要があります。ただし、getter メソッドのシグネチャはサブクラスごとに異なります。たとえば、あるときは整数を返す必要があり、あるときは bool 型を返す必要があります。

ヒントをください、この問題は私を夢中にさせます :/

4

4 に答える 4

1

これが「良い」習慣であるかどうかはよくわかりませんが、これを解決する1つの方法があります。これの利点の 1 つは、間違ったタイプのデータを取得しようとすると、カスタム エラー メッセージを取得できることです。そして、キャストを避けることができます (私はそれらのファンではありません)。

class DataInterface
{
public:
  DataInterface();
  ~DataInterface();

  virtual void FetchData(void) = 0;
  virtual void ProcessData(void) = 0;
  virtual void ClearData(void) = 0;
  virtual int getIntData() { // Error message }
  virtual bool getBoolData() { // Error message }
};

class BinaryData: public DataInterface
{
public:
    BinaryData();
    ~ BinaryData();

    virtual void FetchData(void);
    virtual void ProcessData(void);
    virtual void ClearData(void);
    virtual int getIntData() { // Error message }
    virtual bool getBoolData() { return m_boolData; }

private:
    bool m_boolData;
}

class IntegerData: public DataInterface
{
public:
    IntegerData();
    ~ IntegerData();

    virtual void FetchData(void);
    virtual void ProcessData(void);
    virtual void ClearData(void);
    virtual int getIntData() { return m_intData; }
    virtual bool getBoolData() { // Error message }

private:
    int m_intData;
}


int main()
{
    int IntegerData;
    bool BoolData;
    DataInterface *pData1 = new BinaryData();
    DataInterface *pData2 = new IntegerData();  

    pData1->FetchData();
    pData2->FetchData();

    pData1->ProcessData();
    pData2->ProcessData();

    // now I want to get the data of pData1 and pData2, for example to write it into a file, show in visualization, ...
    IntegerData = pData2->GetIntData();
    BoolData = pData1->GetBoolData();
    BoolData = pData2->GetBoolData() // This will tell you that you are trying to get bool from int class.
}


テンプレートを使用して処理する方法の 1 つを次に示します。

using namespace std;

template<typename T>
class DataInterface
{
public:
    DataInterface(T d) : data(d) {}

    virtual T GetData() = 0;
protected:
    T data;
};

class BinaryData : public DataInterface<bool>
{
public:
    BinaryData(bool b) : DataInterface<bool>(b) {} 

    virtual bool GetData() {return data;}

};

class IntegerData: public DataInterface<int>
{
public:
    IntegerData(int i) : DataInterface<int>(i) {} 

    virtual int GetData() {return data;}

};


int main()
{
    int myint;
    bool mybool;
    DataInterface<bool> *pData1 = new BinaryData(true);
    DataInterface<int> *pData2 = new IntegerData(1);  


    // now I want to get the data of pData1 and pData2, for example to write it into a file, show in visualization, ...
    myint = pData2->GetData();
    mybool = pData1->GetData();

    cout<<myint<<" "<<mybool<<endl;
}
于 2013-03-29T18:34:30.840 に答える
1

データを別のエンティティに渡したい場合は、データを抽象化する必要があります。これを実現するには、次の 2 つの一般的な方法があります。

1: を使用しvoid*ます。

class DataInterface
{
public:
   ...
   virtual void* GetData() = 0;
};

class BinaryData: public DataInterface
{
public:
   virtual void* GetData() { return &m_boolData; }

private:
    bool m_boolData;
};

次のようにmain使用します。

int main()
{
    bool BoolData;
    DataInterface *pData1 = new BinaryData();

    pData1->FetchData();
    pData1->ProcessData();
    BoolData = *(bool*))pData1->GetData());
}

このアプローチの利点は、その単純さにあります。欠点は、オブジェクトの内部への直接アクセス (カプセル化の解除) と、ポリモーフィズムの誤用 (最終的に具体的な派生物に関連する型にキャストする場合、なぜインターフェイスが必要なのですか?) です。

2:

より堅牢な方法は、具体的なオブジェクトから生データをクライアントに送信するのではなく、クライアントとの通信をオブジェクトの追加の役割にすることです。

class DataInterface
{
public:
   ...
   virtual void SendData() = 0;
};

class BinaryData: public DataInterface
{
public:
   ...
   virtual void SendData() 
   {
        //do your stuff here, you know the exact type of your data
   }
};

int main()
{
    bool BoolData;
    DataInterface *pData1 = new BinaryData();

    pData1->FetchData();
    pData1->ProcessData();
    pData1->SendData();
}

これは非常に単純な例ですが、アイデアを示していることに注意してください。通常、実際の使用例では、クライアントをクラスに登録し、定義されたインターフェイスを介してデータをクライアントに送信します。

于 2013-03-29T19:02:37.140 に答える
1

GetData()各派生クラスで非仮想メンバーを作成します。次に、オブジェクトの実際のクラスが確実にわかっている場合は、単純に静的キャストを実行して を呼び出すことができますGetData()

int intData = static_cast<IntegerData*>(pData2)->GetData();

クラスがわからない場合は、動的キャストを実行してその結果を確認する必要があります。

if (IntegerData* _pData2 = dynamic_cast<IntegerData*>(pData2))
{
  int intData = _pData2->GetData();
  // Do stuff with the int
}
else if (BinaryData* _pData2 = dynamic_cast<BinaryData*>(pData2))
{
  bool binaryData = _pData2->GetData();
  // Do stuff with the bool
}
于 2013-03-29T18:29:17.997 に答える
0

これを実現する非常に簡単な方法は、バリアント型を返すように基本クラスを設計することです。バリアントは、異種のタイプのセットからオブジェクトを保持する識別共用体コンテナーです ( http://www.boost.org/doc/libs/1_59_0/doc/html/variant.htmlを参照)。完全な例を次に示します。

#include <iostream>
#include <algorithm>
#include <boost/variant.hpp>

#include <memory>

using namespace std;

class DataInterface
{
public:
    DataInterface(){};
    virtual ~DataInterface(){};

    virtual void FetchData(void) = 0;
    virtual void ProcessData(void) = 0;
    virtual void ClearData(void) = 0;
    virtual boost::variant<bool,int,double,std::string> GetData()=0;
};

class IntResult : public DataInterface{
public:
    IntResult() : resultInt(0){};
    ~IntResult(){};

    virtual void FetchData() override {resultInt = 10;};
    virtual void ProcessData() override {resultInt *= 10;}
    virtual void ClearData() override {resultInt = 0;};

    virtual boost::variant<bool,int,double,std::string> GetData()override{
        return resultInt;
    };

private:
    int resultInt;
};


class StringResult : public DataInterface{
public:
    StringResult() : resultString(""){};
    ~StringResult(){};

    virtual void FetchData() {
        resultString= "Hello World";
    }

    virtual void ProcessData() override {
        std::transform(resultString.begin(), resultString.end(),resultString.begin(), ::toupper);
    }

    virtual void ClearData() override {resultString = "";}

    virtual boost::variant<bool,int,double,std::string> GetData() override {
        return resultString;
    };


private:
    std::string resultString;
};





int main() {

    DataInterface* data;

    IntResult* intResult = new IntResult;
    StringResult* stringResult = new StringResult;

    data = intResult;

    data->FetchData();
    data->ProcessData();

    switch(data->GetData().which()){
        case 0:
            std::cout << "found bool: " << boost::get<bool>(data->GetData()) << std::endl;
            break;
        case 1:
            std::cout << "found int: " << boost::get<int>(data->GetData()) << std::endl;
            break;
        case 2:
            std::cout << "found double: " << boost::get<double>(data->GetData()) << std::endl;
            break;
        case 3:
            std::cout << "found string: " << boost::get<std::string>(data->GetData()) << std::endl;
            break;
        default:
            break;
    }

    data = stringResult;

    data->FetchData();
    data->ProcessData();

    switch(data->GetData().which()){
        case 0:
            std::cout << "found bool: " << boost::get<bool>(data->GetData()) << std::endl;
            break;
        case 1:
            std::cout << "found int: " << boost::get<int>(data->GetData()) << std::endl;
            break;
        case 2:
            std::cout << "found double: " << boost::get<double>(data->GetData()) <<  std::endl;
            break;
        case 3:
            std::cout << "found string: " << boost::get<std::string>(data->GetData()) << std::endl;
            break;
        default:
            break;
    }

    delete intResult;
    delete stringResult;

    return 0;
}

あなたの場合、bool は暗黙的に int に変換可能であるため、常に単に int を返すことができることに注意してください。真に異種の型を返す必要がある場合は、バリアント アプローチが機能します。同様に、boost any を返すこともできます。これにより、型の異種結合を均一に操作することもできます ( http://www.boost.org/doc/libs/1_59_0/doc/html/any.htmlを参照)。最後に、boost に依存したくない場合は、識別可能な型のセットを保持できる独自のバリアント型をロールアウトすることはそれほど難しくありません。

于 2015-11-20T20:13:36.080 に答える