我试图实现从一个基类派生的数据类。
每个派生类都有不同的数据字段。
因此,每个实例都必须根据数据实例的类型进行不同的处理。
我为此制作了一个示例代码。
#include <iostream>
#include <algorithm>
#include <boost/shared_ptr.hpp>
using namespace std;
enum DataTypes{
EMPTY = 0,
TYPE1,
TYPE2
};
class DataBase {
public:
DataBase():type(EMPTY){}
virtual ~DataBase(){}
DataTypes getType() const{return type;}
protected:
DataBase(DataTypes n):type(n){}
DataTypes type;
};
class DataType1 :public DataBase {
public:
DataType1(): DataBase(TYPE1){}
string data_for_class1;
};
class DataType2 :public DataBase {
public:
DataType2(): DataBase(TYPE2){}
string data_for_class2;
};
boost::shared_ptr<DataBase> createInstance(int n){
boost::shared_ptr<DataBase> p;
if(n == 1){
p.reset(new DataType1);
boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
temp->data_for_class1 = "[Data for DataType1]";
}
else if(n==2){
p.reset(new DataType2);
boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
temp->data_for_class2 = "[Data for DataType2]";
}
return p;
}
int main() {
boost::shared_ptr<DataBase> p = createInstance(2);
try{
/*
if p is an instance of DataType1
process p as DataType1;
else if p is an instance of DataType2
process p as DataType2;
else
throw exception("Empty data");
*/
}catch(runtime_error& e){
cerr<<e.what()<<endl;
}
return 0;
}
应填写try-catch表达式中的注释部分。
在努力思考之后,我得到了两个可能的解决方案。
type
类DataBase
字段
为此,
DataBase
课程应该有type
字段。 DataBase
派生的每个类都负责使用适当的参数调用构造函数(DataBase(DataTypes n)
)。代码:
switch (p->getType()){
case TYPE1:
{
cout<<"This is an instance of DataType1"<<endl;
boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
cout<<temp->data_for_class1<<endl;
}
break;
case TYPE2:
{
cout<<"This is an instance of DataType2"<<endl;
boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
cout<<temp->data_for_class2<<endl;
}
break;
case EMPTY: default:
throw runtime_error("Data is empty");
}
dynamic_cast
与case1相比,这种方法
DataBase
具有用于类型检查的附加字段。 dynamic_cast
是做这件事的好方法。代码:
if(boost::dynamic_pointer_cast<DataType1>(p)){
cout<<"This is an instance of DataType1"<<endl;
boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
cout<<temp->data_for_class1<<endl;
}
else if(boost::dynamic_pointer_cast<DataType2>(p)){
cout<<"This is an instance of DataType2"<<endl;
boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
cout<<temp->data_for_class2<<endl;
}
else{
throw runtime_error("Data is empty");
}
以上两种方式,我都可以获得工作代码。但我不确定这是否是正确的做法。
如果上述代码存在潜在问题,或者您知道更好的解决方案,请分享您的好主意。
感谢。
答案 0 :(得分:0)
如果您使用的是polymorphishm,那么最好利用提供的语言功能来支持。因此#2优于#1
您可能还想避免使用dynamic_cast并使用语言提供的运行时类型检查 - &#34; typeid(* p).name&#34;。这将为您提供由&#34; p&#34;。
表示的对象使用这种方法重写您的示例:
if(typeid(*p).name == "DataType1"){
cout<<"This is an instance of DataType1"<<endl;
boost::shared_ptr<DataType1> temp = boost::static_pointer_cast<DataType1>(p);
cout<<temp->data_for_class1<<endl;
}
else if(typeid(*p).name == "DataType2"){
cout<<"This is an instance of DataType2"<<endl;
boost::shared_ptr<DataType2> temp = boost::static_pointer_cast<DataType2>(p);
cout<<temp->data_for_class2<<endl;
}
else{
throw runtime_error("Data is empty");
}
答案 1 :(得分:0)
还有解决方案#3,其中存储了虚拟getType
(并且没有type
字段。
class DataBase {
public:
DataBase() {}
virtual ~DataBase() {}
virtual DataTypes getType() { return EMPTY; } // or '= 0;'
};
class DataType1 :public DataBase {
public:
//...
DataTypes getType() { return TYPE1; };
};
class DataType2 :public DataBase {
public:
//...
DataTypes getType() { return TYPE2; };
};
答案 2 :(得分:0)
方式1更有效,因为它不使用运行时类型信息。
答案 3 :(得分:0)
给出的所有选项都不是很好的设计,因为它们似乎处理数据库,但实际上只适用于DataBase的子类型,这些子类型也在一些特殊的受支持类型列表中。演员们放弃了。更正式地说,这违反了Liskov替代原则。
因为该类被称为&#34; DataBase&#34;,我猜你不想在DataBase中放入一个纯粹的虚拟进程(...)方法,即子类将实施
在这种情况下,您应该使用双重发送:https://en.wikipedia.org/wiki/Double_dispatch
例如,您可以像这样创建一个DBConsumer接口:
class DBConsumer
{
public:
virtual void accept(DataType1 &data) = 0;
virtual void accept(DataType2 &data) = 0;
}
然后,在您的基类中:
class DataBase
{
...
virtual void sendTo(DBConsumer &consumer) = 0;
...
}
并在每个子类中实现,以调用适当的重载:
class DataType1
{
...
void sendTo(DBConsumer &consumer)
{
consumer.accept(*this); //calls DataType1 overload
}
...
}
class DataType2
{
...
void sendTo(DBConsumer &consumer)
{
consumer.accept(*this); //calls DataType2 overload
}
...
}
现在,如果要处理DataTypes,可以实现为每个子类实现accept()重载的使用者:
int main()
{
...
MyDBConsumer myConsumer;
boost::shared_ptr<DataBase> p = createInstance(2);
p->sendTo(myConsumer);
}
多田!没有强制转换,也没有支持类型的魔术列表。当有人添加新的DataType3子类时,编译器将确保所有DBConsumers 支持它。