我有一个问题,我会以类似的方式一次又一次地遇到。
例如:
我有一个抽象基类,它充当一系列具体类的接口,这些类充当数据容器。
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类型。
请给我一个提示,这个问题让我疯狂:/
答案 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有任何依赖性,那么推出自己的变体类型并不是非常困难,它可以保存区分的类型集。