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 方法。但是 getter 方法的签名会因子类而异。例如一次我需要返回一个整数,一次我需要返回一个布尔类型。

请给我一个提示,这个问题让我发疯:/

4

4 回答 4

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 回答
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.
}


这是使用模板处理它的一种方法。

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

如果要将数据传递给另一个实体,则需要对其进行抽象。有两种常见的方法来实现这一点:

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 回答
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 回答