如何使用基类指针获取子类的属性

时间:2013-03-29 18:02:01

标签: c++ class inheritance subclass virtual

我有一个问题,我会以类似的方式一次又一次地遇到。

例如:

我有一个抽象基类,它充当一系列具体类的接口,这些类充当数据容器。

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继承的接口。但是他们拥有不同的属性来保存他们的数据。到目前为止一切都很好。

我可以使用main函数中的类:

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方法的签名将从子类到子类不等。例如,有一次我需要返回一个整数,有一次我需要返回一个bool类型。

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

4 个答案:

答案 0 :(得分: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
}

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


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

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

答案 2 :(得分: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();
}

注意,这是一个非常剥离的例子,但它证明了这个想法。通常,在实际用例中,您将向您的类注册客户端,并通过定义的接口将数据发送给它们。

答案 3 :(得分: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,因此您可以随时返回整数。如果您需要返回真正的异构类型,那么变体方法将起作用。等效地,您可以返回任何提升,这也可以让您统一操作类型的异构联合(请参阅http://www.boost.org/doc/libs/1_59_0/doc/html/any.html)。最后,如果您不希望对boost有任何依赖性,那么推出自己的变体类型并不是非常困难,它可以保存区分的类型集。