我正在编写一个“设备驱动程序”(C ++ 14),它可以处理用于不同版本设备的协议的多个版本。该设备驱动程序在外部PC上运行,该PC通过基于HTTP的协议通过以太网与设备通信。所有版本都有通用的功能,但是某些功能可能在协议的某些版本中是附加的。
下面是一个示例:
class ProtocolBase {
public:
virtual void reset_parameters() {
std::cout << "reset parameters" << std::endl;
}
virtual void set_parameters() {
std::cout << "set parameters" << std::endl;
}
};
class ProtocolV1 : public ProtocolBase
{
public:
void set_parameters() override {
std::cout << "set parameters for V1" << std::endl;
}
};
class ProtocolV2 : public ProtocolBase
{
public:
void set_parameters() override {
std::cout << "set parameters for V2" << std::endl;
}
void reset_parameters() {
std::cout << "reset parameters for V2" << std::endl;
}
void do_V2() {
std::cout << "doing V2" << std::endl;
}
};
下面是main
:
int main(int argc, char const *argv[])
{
int version = std::atoi(argv[1]);
std::unique_ptr<ProtocolBase> protocol = std::make_unique<ProtocolV1>();
switch (version)
{
case 1:
/* do nothing at the moment */
break;
case 2:
protocol.reset(new ProtocolV2);
break;
default:
break;
}
protocol->reset_parameters();
if(ProtocolV2* p = dynamic_cast<ProtocolV2*>(protocol.get())) { //not sure about this
p->do_V2();
}else {
std::cout << "This functionality is unavailable for this device" << std::endl;
}
protocol->set_parameters();
return 0;
}
我觉得使用dynamic_cast
并不是最好的选择。期待一些反馈。
编辑:根据@ Ptaq666的回答,我将ProtocolBase
和ProtocolV2
修改为:
class ProtocolBase {
public:
virtual void do_V(){
std::cerr << "This functionality is unavailable for this device" << std::endl;
}
};
class ProtocolV2 : public ProtocolBase
{
public:
void do_V() override {
std::cout << "doing V2" << std::endl;
}
};
有了这个,不再需要dynamic_cast
了,尽管基类必须知道所有功能。这似乎是目前最好的解决方案。
答案 0 :(得分:0)
通常,这取决于派生类ProtocolV1
和ProtocolV2
的形成方式以及数据成员和天气(如果各自的成员函数是否要使用不同的数据成员)!
原因很简单,因为不依赖于成员数据,成员函数仅对调用它们的对象的类型敏感,而对它们的值/状态不敏感!
就像(非成员)函数重载一样:
void function(ProtocolV1 *){
std::cout << "set parameters for V1" << std::endl;
}
void function(ProtocolV2 *){
std::cout << "set parameters for V2" << std::endl;
}
然后通过类型为ProtocolV1 *
的指针调用一次,并使用类型为ProtocolV2 *
的空指针调用一次。
如果您喜欢问题中提出的替代用法,您甚至可以使用C样式强制转换: 结果是 SAME !
!最后,如果您要调用成员函数然后从中调用另一个函数,该函数需要一些数据成员,那么在各个派生类中,数据成员之间是不同的,那么您就不能使用任何强制类型转换,除非您引入某种形式的补偿来填充强制类型中未显示的数据!
祝你好运!
答案 1 :(得分:0)
就像在大多数情况下选择适当的系统架构一样,答案是
“这取决于” :)。最舒适的解决方案是引入特定于协议的行为
for ($i = 110009; $i <= 114993; $i++) {
echo $i . "\n";
}
子类在其构造函数中的位置
ProtocolBase
如果由于某些原因您无法执行此操作,则有可能将class ProtocolV2 : public ProtocolBase
{
public:
ProtocolV2::ProtocolV2(args) {
// set some params that will determine when do_V2() is called
// it can be some numeric setting, a callback, or similar
}
void set_parameters() override {
// you can use V2 behavior here maybe?
std::cout << "set parameters for V2" << std::endl;
}
void reset_parameters() override {
// or here maybe?
std::cout << "reset parameters for V2" << std::endl;
}
private:
void do_V2() {
std::cout << "doing V2" << std::endl;
}
};
保持公开状态
非虚拟方法,但必须在传递do_V2()
作为指向ProtocolV2
的指针之前进行调用
到将使用它的系统。当然限制是ProtocolBase
只能在外部调用
您的系统范围,可能无法真正解决问题。
另一种选择是将do_V2
实际移至界面:
do_V2()
,并默认将其实现为“不支持”行为。只有class ProtocolBase {
public:
virtual void reset_parameters() {
std::cout << "reset parameters" << std::endl;
}
virtual void set_parameters() {
std::cout << "set parameters" << std::endl;
}
virtual void do_V2() {
std::cout << "not supported" << std::endl;
}
};
会实现此行为
作为协议的有效部分。
最后,如果以上都不是,您当然可以按照建议的方式使用dynamic_cast。
我个人会尽量避免使用ProtocolV2
,因为我的办公室同事肯定会开始滥用它,
但在某些情况下,这是一个正确的解决方案。
此外,如果您决定强制转换指针,请将dynamic_cast
与std::shared_ptr
一起使用,而不要从dynamic_pointer_cast
访问原始指针。