【问题标题】:How to use polymorphism to execute command on objects, which have no common base class?如何使用多态对没有公共基类的对象执行命令?
【发布时间】:2020-11-21 07:23:19
【问题描述】:

我通过 json 接收命令,我将其插入管道。因此,您必须具有相同的基类。 管道由管道处理程序读取,一些命令由管道处理程序使用,其他命令必须向下传递给设备,该设备是管道处理程序的成员。我可以这样做:

class Command{};
class HandlerCommand : public Command {
   void execute(Handler* h);
};
class DeviceCommand : public Command {
   void execute(Device* d);
};

Command* c = pipe.receive();
if (const auto hc  = dynamic_cast<const HandlerCommand*>(c)) { hc.execute( **handlerptr** ); }
else if (const auto dc  = dynamic_cast<const DeviceCommand*>(c))  { dc.execute( **deviceptr** );}

Device 和 pipehandler 不应该有相同的基础,因为它们没有共同的方法、字段,它们在概念上是不同的。 有没有办法避免在这里使用动态转换。我在想也许有一些巧妙的设计模式可以解决这个问题,但我想不出更好的解决方案。

编辑:没有从命令派生 DeviceCommand 和 HandlerCommand,修复了这个。

【问题讨论】:

  • dynamic_cast 在这里不起作用。对象必须从Command 派生并且至少有一个虚函数才能使用它。听起来你需要的是std::variant
  • 使用变体并访问

标签: c++ architecture


【解决方案1】:

你不能使用两个没有共同点的事物的多态性。您将需要相同的基类/接口:在您的情况下为Command。如上所述,您的基类需要一个必须由派生类实现的纯虚函数。我将使用Command * clone()const 原型,这在以后可能会非常有用。请引入基类的虚拟析构函数,否则,追踪此内存错误可能会很麻烦。请注意,关于您的dynamic_cast 成员函数execute,必须是const。你可以试试这个:

#include <iostream>
#include <vector>

class Handler
{
public:
    Handler(){}
};

class Device
{
public:
    Device(){}
};

enum class CommandType{Handler,Devise};

class Command 
{
public:
    virtual ~Command(){}
    virtual Command*clone()const = 0;
    virtual CommandType getType()const = 0;
};
class HandlerCommand : public Command {
public:
    HandlerCommand():Command(){}
    void execute(Handler* h) const
    {
        std::cout << __FUNCTION__<<"\n";
    }

    HandlerCommand*clone()const { return new HandlerCommand(*this); }
    CommandType getType()const { return CommandType::Handler; }
};
class DeviceCommand : public Command{
public:
    DeviceCommand():Command(){}
    void execute(Device* d)const
    {
        std::cout << __FUNCTION__<<"\n";
    }
    DeviceCommand*clone()const { return new DeviceCommand(*this); }
    CommandType getType()const { return CommandType::Devise; }
};


int main()
{
    Device dev;
    Handler handler;
    std::vector<Command*> pipe{ new HandlerCommand(), new DeviceCommand() };

    while (!pipe.empty())
    {
        Command* c = pipe.back(); 
        if (c->getType() ==  CommandType::Handler) { static_cast<const HandlerCommand*>(c)->execute(&handler); }
        else if (c->getType() == CommandType::Devise ) { static_cast<const DeviceCommand*>(c)->execute(&dev); }

        delete c;

        pipe.pop_back();

    }

    std::cin.get();


}

输出:

DeviceCommand::execute
HandlerCommand::execute

2.0 版使用std::variant。你至少需要 C++17 来编译它。请注意,单个pipe 容器可以专门包含变体中提到的类之一。因此不再需要铸造,但您将需要两个管道。因此,我引入了一个时间戳变量。

#include <iostream>
#include <vector>
#include <variant>

class Handler
{
public:
    Handler() {}
};

class Device
{
public:
    Device() {}
};




class HandlerCommand  {
    int ts;
public:
    HandlerCommand(int _ts):ts(_ts) {}
    void execute(Handler* h) const
    {
        std::cout << ts << ": "<< __FUNCTION__ << "\n";
    }
    int timeStamp()const { return ts; }
};
class DeviceCommand  {
    int ts;
public:
    DeviceCommand(int _ts) :ts(_ts) {}
    void execute(Device* d)const
    {
        std::cout << ts << ": " << __FUNCTION__ << "\n";
    }
    int timeStamp()const { return ts; }
};


using Command = std::variant<HandlerCommand, DeviceCommand>;

int main()
{
    Device dev;
    Handler handler;
    std::vector<Command> hcPipe{HandlerCommand(2),HandlerCommand(5)};
    std::vector<Command> dcPipe{DeviceCommand(1),DeviceCommand(4)};

    Command single = DeviceCommand(0);

    if (single.index() == 0)
    {
        std::get<HandlerCommand>(single).execute(&handler);
    }
    else
    {
        std::get<DeviceCommand>(single).execute(&dev);
    }


    while (!hcPipe.empty() || !dcPipe.empty())
    {
        if (!hcPipe.empty() && (dcPipe.empty() || std::get<HandlerCommand>(hcPipe.front()).timeStamp() < std::get<DeviceCommand>(dcPipe.front()).timeStamp()))
        {
            std::get<HandlerCommand>(hcPipe.front()).execute(&handler);
            hcPipe.erase(hcPipe.begin());
        }
        else
        {
            std::get<DeviceCommand>(dcPipe.front()).execute(&dev);
            dcPipe.erase(dcPipe.begin());
        }

    }


    std::cin.get();
}

输出:

0: DeviceCommand::execute
1: DeviceCommand::execute
2: HandlerCommand::execute
4: DeviceCommand::execute
5: HandlerCommand::execute

【讨论】:

  • 是的,这是我的意思的原始问题,只是从 Command 中省略了基类,我的问题是这里是否有办法摆脱动态演员表
  • a 添加了第二个版本,没有 dynamic_cast 但具有通用基类。希望有帮助
猜你喜欢
  • 2013-02-23
  • 1970-01-01
  • 2023-02-09
  • 2013-12-18
  • 2020-09-30
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
相关资源
最近更新 更多