C ++设计:重载/覆盖许多函数,清理方法?

时间:2016-06-10 21:03:32

标签: c++ oop overloading override

我试图在这里实现的情况是一个Base类,它有一个函数(让它称之为modify_command),它可以接受许多不同的类型,因此派生类可以根据需要实现modify_command函数。 现在我在基类中有这些东西:

class Base 
{
    template<typename Command>
    void modify_command(Command cmd)
    { 
        std::cout << "Modify command called with unimplemented command type:" << typeid(cmd).name();
    }

    virtual void modify_command(SpecificCommandA cmd)
    {
         modify_command<SpecificCommandA>(cmd); // Calls the templated function
    }

    virtual void modify_command(SpecificCommandB cmd)
    {
         modify_command<SpecificCommandB>(cmd); // Calls the templated function
    }

    // etc.
};

然后在派生类中:

class Derived : public Base
{
    virtual void modify_command(SpecificCommandA cmd)
    {
        cmd.x = 1;
        cmd.y = 2;
    }
}

显然虚拟模板函数不是可能的,所以在某种形式下,我将不得不列出函数声明,以便进行多种参数可能性,这肯定会使类定义混乱,并且可能使(很长一段时间)要处理的其他命令类型

使用模板化函数的目的是编译这种情况,而无需定义modify_command(SpecificCommandC)来记录错误:

Base * base = new Derived();
SpecificCommandA a;
SpecificCommandB b;
SpecificCommandC c;
base->modify_command(a); //Set's x and y
base->modify_command(b); //Outputs that command type is unimplemented
base->modify_command(c); //Outputs that command type is unimplemented

我真的很讨厌我的工作方式,是否有人建议如何清理/重新实现?随着软件的成熟,命令数量将继续增长,因此必须具备可扩展性。

编辑:语法

3 个答案:

答案 0 :(得分:1)

我不确定这是否是你想要的,但我会继续我的想法:

用于在类型列表/可变参数参数列表中查找类型的小元程序。需要确定命令是否是实现的一部分。

namespace meta {
template <typename... T> struct list{};
template <typename F, typename T> struct has_type;

template <typename F>
struct has_type<F, list<>> {
  using type = typename std::false_type;
  static constexpr bool value = false;
};

template <typename F, typename... T>
struct has_type<F, list<F, T...>> {
  using type = typename std::true_type;
  static constexpr bool value = true;
};

template <typename F, typename H, typename... T>
struct has_type<F, list<H,T...>> {
  using type = typename std::false_type;
  static constexpr bool value =
    std::is_same<F, typename std::decay<H>::type>::value ? true : has_type<F, list<T...>>::value;
};
}

现在定义具有公共基类的命令:

struct CommandBase {};

struct CommandA: CommandBase {};
struct CommandB: CommandBase {};
struct SomeCommandType: CommandBase {};
struct CommandC: CommandBase {};

using AvailableCommmands = meta::list<CommandA, CommandB, SomeCommandType>;

AvailableCommmands类型决定应该发送什么类型的not implemented消息。如果AvailableCommmands meta :: list中不存在特定的coomand类型,则应该打印该类型not implemeneted消息。

其余代码(基础+派生):

class Base
{
public:
  template <typename T>
  void on_modify_command(T cmd) {
    do_on_modify_command(cmd, typename meta::has_type<T, AvailableCommmands>::type());
  }
private:
  virtual void do_on_modify_command(CommandBase&, std::true_type) = 0;
  virtual void do_on_modify_command(CommandBase& b, std::false_type) {
    std::cout << "Not implemented" << std::endl;
  }

};

class Derived: public Base
{
public:
  void do_on_modify_command(CommandBase& cmd, std::true_type) {
    std::cout << "Specialized command implementation" << std::endl;
    impl(*static_cast<SomeCommandType*>(&cmd));
  }
  void impl(SomeCommandType cmd) {
    std::cout << "huh!!" << std::endl;
  }

};

int main() {
  CommandA ca;
  Base* b = new Derived;
  b->on_modify_command(ca);
  CommandC cc;
  b->on_modify_command(cc);

  return 0;
}

代码可以理解为凌乱,我相信必须有更好的方法来实现它。

答案 1 :(得分:1)

不幸的是,什么可以解决您的问题是虚拟模板方法,这是不可能的。

这是一个更多的 C-ish 解决方案,它可以解决这个限制:

#include<unordered_map>
#include<functional>
#include<memory>
#include<iostream>
#include<utility>

struct BaseCommand {
    static int counter;
};

int BaseCommand::counter = 0;

template<class T>
struct Command: BaseCommand {
    static int type() {
        static const int t = ++counter;
        return t;
    }
};

struct SpecificCommand1: Command<SpecificCommand1> {};
struct SpecificCommand2: Command<SpecificCommand2> {};

class Base {
    struct Handler {
        virtual void operator()(BaseCommand &cmd) = 0;
    };

    template<typename T>
    struct THandler: Handler {
        std::function<void(T)> func;
        void operator()(BaseCommand &cmd) override {
            func(static_cast<T&>(cmd));
        }
    };
protected:
    template<typename T>
    void assign(std::function<void(T)> f) {
        auto handler = std::make_unique<THandler<T>>();
        handler->func = f;
        handlers[T::type()] = std::move(handler);
    }

public:
    template<typename Command>
    void modifyCommand(Command cmd) {
        auto it = handlers.find(Command::type());
        if(it == handlers.end()) {
            std::cout << "Modify command called with unimplemented command type: " << Command::type();
        } else {
            auto &h = *(it->second);
            h(cmd);
        }
    }

private:
    std::unordered_map<int, std::unique_ptr<Handler>> handlers;
};

class Derived: public Base {
public:
    Derived() {
        std::function<void(SpecificCommand1)> f =
            [](SpecificCommand1) {
                std::cout << "handler for SpecificCommand1" << std::endl;
            };

        assign(f);
    }
};

int main() {
    Base *b = new Derived;
    b->modifyCommand(SpecificCommand1{});
    b->modifyCommand(SpecificCommand2{});
}

基本思想是在运行时为命令提供一个数字类型(可以使用CRTP惯用法 - 请参阅BaseCommandCommand模板类)。 如果可以访问此值,则需要创建一个类型擦除的处理程序来处理要为其提供特定实现的命令(请参阅assign和{{1 }} / Handler)。
一旦正确设置了所有部分,您只需在派生类中设计和初始化这些处理程序。因为它可以使用THandler来完成,您可以使用lambda,公共或私有成员方法,静态方法等作为处理程序。 有关详细信息,请参阅std::function的构造函数。

答案 2 :(得分:0)

我可能没有完全理解你的问题,但我找到了一个解决方案,解决了如何在派生类中允许重载的问题,这些问题将作为&#34;虚函数&#34;为模板。 我做了一个小改动:SpecificCommandASpecificCommandB有一个共同的祖先类Command,如果在你的代码中它们不是,那么只需创建一个Command类并有struct SpecificAPrime: Common, SpecificCommandA {};

之类的东西

首先是代码:

#include <typeindex>
#include <unordered_map>

struct Command { virtual ~Command() {}; };

struct SpecificCommandA: Command {};
struct SpecificCommandB: Command {};
struct SpecificCommandC: Command {};


struct Base {
    virtual ~Base() {}; 

    virtual void modify_command(Command &c, std::type_index); // default

    void modify_command(Command &c) {
        modify_command(c, std::type_index(typeid(c)));
    }
};

template<typename Der, typename SpecC> struct Modifier {
    static void execute(Der &d, Command &c) {
        d.modify_command(static_cast<SpecC &>(c)); 
    }
};

struct Derived: Base {
    using Base::modify_command; // <- Because there are overloads
    virtual void modify_command(SpecificCommandB &);
        // Put here the specific code you want for SpecificCommandB.
        // Note that this can be overriden in derived classes

    void modify_command(Command &c, std::type_index ti) override {
        using callable = void (*)(Derived &, Command &);
        const static std::unordered_map<std::type_index, callable> derived_map =
        {
            { std::type_index(typeid(SpecificCommandB)), Modifier<Derived, SpecificCommandB>::execute }
        };
        auto find_result = derived_map.find(ti);
        if(derived_map.end() == find_result) { Base::modify_command(c, ti); }
        else { (*find_result->second)(*this, c); }
    }
};

这个想法如下:由于现在Commands有一个共同的祖先,并且是多态的,因此可以使用动态多态而不是模板,包括typeid运算符。

现在modify_command(Command &) 不是模板,也不是多态函数,它的作用是找到命令的type_index并将其转发给多态函数。多态函数modify_command(Command &, type_index)的实现在内部具有type_index的映射,它们知道如何具体修改。如果给定的type_index不在映射中,则默认为基类实现,如果type_index在映射中,则调用映射元素,这只是模板函数的实例化调用给定的特定过载。

从某种意义上说,这是多态类型切换。

这是一个完整的,正在运行的示例:http://ideone.com/K1Xmdk