C++设计:重载/重写许多函数,如何清理
C++ Design: Overloading/Overriding many many functions, way to clean up?
我在这里尝试实现的例子是一个基类,它有一个函数(让我们称之为modify_command),它可以虚拟地接受许多不同的类型,因此派生类可以根据自己的意愿实现modify_ccommand函数。现在我在基类中有一些类似的东西:
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
我真的很讨厌我的工作方式,有人对如何清理/重新实施有什么建议吗?随着软件的成熟,命令的数量将继续增长,因此扩展性是必须的。
编辑:语法
不幸的是,解决问题的是一个虚拟模板方法,这是不可能的。
这里有一个更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习惯用法来实现——请参阅BaseCommand
和Command
模板类)
可访问此值后,只需创建一个类型的擦除处理程序来处理要为其提供特定实现的命令(请参见assign
和Handler
/THandler
)
一旦正确设置了所有部分,就只需要在派生类中设计和初始化这些处理程序。由于它可以使用std::function
来完成,您可以使用lambda、公共或私有成员方法、静态方法等作为处理程序。
有关详细信息,请参阅Derived
的构造函数。
我不确定这是否是你想要的,但我正在进行我所提出的:
在类型列表/变量参数列表中查找类型的小型元程序。需要确定命令是否是实现的一部分。
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::列表中不存在特定的命令类型,则应打印该类型的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;
}
代码是可以理解的混乱,我相信一定有一些更好的方法来做它
我可能没有完全理解你的问题,但我提出了一个解决方案,解决了如何允许派生类重载的问题,这些派生类将作为模板的"虚拟函数"工作。我做了一个小改动:SpecificCommandA
和SpecificCommandB
有一个共同的祖先类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); }
}
};
其想法如下:由于现在命令有一个共同的祖先,并且是多态的,因此可以使用动态多态性来代替模板,包括typeid运算符。
现在modify_command(Command &)
不是模板,也不是多态函数,它所做的是找到命令的type_index
并将其转发给多态函数。多态函数modify_command(Command &, type_index)
的实现在内部具有type_index的映射,它们知道如何具体修改该映射。如果给定的type_index
不在映射中,则它们默认为基类实现,如果type_index
在映射中则调用映射元素,这只是调用给定特定重载的模板函数的实例化。
从某种意义上说,这是一个多态类型的开关。
下面是一个完整的运行示例:http://ideone.com/K1Xmdk
- 函数何时会在c++中包含stack_Unwind_Resume调用
- rcpp函数中的清理时间很长
- 标准对此指向成员函数类型模板参数有何说明?是我的代码有误,还是 MSVS 16.6 有问题?
- 单例类析构函数无法清理 (SDL_Quit) MinGW
- Clang++ 6.0 内存清理器未报告返回值指示条件分支的函数中的未初始化局部变量
- "virtual"对C++析构函数有何影响?
- 在两个类上协调析构函数,其中一个类需要首先清理
- C++ 友元函数在内存位置上有何不同?
- 指向成员函数的指针与指向数据成员的指针有何不同
- 清理所拥有的(!)字符串成员时,析构函数偶尔崩溃
- 双重列表复制构造函数:与单一列表复制构造函数有何不同
- C++设计:重载/重写许多函数,如何清理
- 如果从 c++ 中的构造函数引发异常,如何清理初始化的资源
- 堆栈必须在函数结束语混淆之前清理干净
- 如何在不清理帧缓冲区的情况下使用 Qt 的 UpdateGL() 函数?
- C++03 12.4/12对通过指针显式调用基类析构函数有何说明
- 如何清理已由虚拟成员函数分配的资源
- 清理C++异常析构函数中的代码
- 缺少复制构造函数与对象切片有何关系
- 用于清理资源的析构函数