提问者:小点点

如何使用多态在对象上执行命令,这些对象没有公共基类?


我正在通过json接收命令,我将这些命令插入到管道中。 因此,thye必须具有相同的基类。 管道由管道处理程序读取,一些命令由管道处理程序使用,其他命令必须向下传递到作为管道处理程序成员的设备。 我可以简单地这样做:

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,修复了这个问题。


共1个答案

匿名用户

你不能使用两个没有共同之处的事物的多态性。 您将需要相同的基类/接口:在您的示例中为命令。 如上所述,您的基类需要一个纯虚函数,该函数必须由派生的类实现。 我将使用命令*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

使用std::variant的2.0版。 您至少需要用C++17来编译它。 注意,单个管道容器可以独占地包含变体中提到的类之一。 所以没有铸件了,但是你需要两根管子。 因此,我引入了一个时间戳变量。

#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