2

我正在编写一个“设备驱动程序”(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 的回答,我修改ProtocolBaseProtocolV2

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了,尽管基类必须知道所有的功能。这似乎是目前最好的解决方案。

4

2 回答 2

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

然后用 type 的指针调用一次,然后用 typeProtocolV1 *的空指针调用一次ProtocolV2 *

如果您喜欢问题中提出的用法的替代方案,您甚至可以使用 C 样式转换:结果是一样

最后,如果您要调用成员函数然后从中调用另一个函数,该函数需要一些数据成员,这在派生类中作为其参数是不同的,那么除非您引入,否则您不能使用任何强制转换某种形式的补偿来填充铸造类型中未呈现的数据!

祝你好运!

于 2019-05-20T08:23:06.967 回答
0

就像在大多数情况下选择合适的系统架构时一样,答案是“取决于”:)。最舒适的解决方案是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;
    }
};

并默认将其实现为“不支持”的行为。只会ProtocolV2将此行为作为协议的有效部分来实现。

最后,如果以上都不行,你当然可以按照你的建议使用dynamic_cast。就我个人而言,我尽量避免dynamic_cast,因为我的办公室同事肯定会开始滥用它,但在某些情况下,这是一个正确的解决方案。

此外,如果您决定强制转换指针,请使用std::shared_ptrwithdynamic_pointer_cast而不是从unique_ptr.

于 2019-05-20T08:38:14.970 回答