想象一下这个简单的基类:
struct simple_http_service
{
virtual reply http_get(…);
virtual reply http_post(…);
virtual reply http_delete(…);
// etc.
};
我想阻止用户从这个类派生而不至少覆盖其中一个并阻止它们实例化simple_http_service
是否有一些很好的方法可以做到这一点?
答案 0 :(得分:5)
这听起来像是一个非常奇怪的约束。通过各种方式保护用户免受不正确的使用,但不要试图禁止你只是“看不到”的东西。如果在没有覆盖任何三个函数的情况下从你的类中派生没有任何意义,那么让用户覆盖他喜欢的任意数量的函数,并相信他不会在没有覆盖的情况下做无意义的事情。任何的功能。用户这样做没有坏处,它只是没有用处。
但是如果你做需要强制执行(再次,我建议你重新思考),那么不要使用虚函数。相反,传递函数指针或函数对象(或std::function
/ boost::function
)回调。使基类看起来像这样:
struct simple_http_service
{
typedef std::function<reply (...)> func_type;
reply http_get(...) { return get_func(...); }
reply http_post(...) { return post_func(...); }
reply http_delete(...) { return delete_func(...); }
// etc.
private:
func_type get_func;
func_type post_func;
func_type delete_func;
};
现在只需添加必要的构造函数(或自由/静态函数,以便命名它们以避免歧义),这样只有在提供至少一个函数对象时才能实例化类。
答案 1 :(得分:1)
我认为所有这些功能都应该是纯虚拟的。您发布的结构实际上是一个接口。如果函数并非都是必需的,那么派生的结构只应为与它们无关的函数提供一个空的实现。
答案 2 :(得分:1)
如果你只想强制抽象的基类,给它一个纯虚拟析构函数,并使你的其他函数成为普通的虚函数。
答案 3 :(得分:0)
如果您知道希望派生类覆盖哪些方法,只需声明该方法pure virtual。
例如,要使http_get成为纯虚拟:
struct simple_http_service
{
virtual reply http_get(…) = 0;
virtual reply http_post(…);
virtual reply http_delete(…);
// etc.
};
答案 4 :(得分:0)
我不太清楚为什么要为其他两个功能提供默认实现,但是对于http请求,至少需要其中之一是用户定义的。 很明显,是否所有功能都在互相使用以使用现有代码实现某些功能。 想象一下该类的示例:
class Cls
{
public:
virtual std::string toString()=0;
virtual std::string serialize()=0;
};
有一个可字符串转换和可序列化的类。但是,如果其中之一未实现,则您想调用第二个,因此可以选择:
class Cls
{
public:
virtual std::string toString() //calls serialize() by default
{
return this->serialize();
}
virtual std::string serialize() //calls toString()
{
return this->toString();
}
virtual ~Cls()=0; //force the class to be abstract
};
但是现在存在从Cls派生但不覆盖至少一个功能的问题。如果未进行覆盖,则在运行时只需输入无限递归即可。如果这是您的问题,那么有一个运行时解决方案,如果发生这种问题,下面的代码将不执行任何操作。
class Cls
{
public:
virtual std::string toString()
{
if ((void*)(this->*(&Cls::serialize)) != (void*)(&Cls::serialize))
{//checks if the current implemetation is not equal to the default one
return this->serialize();
}
else
{
return ""; //default return value
}
}
virtual std::string serialize()
{
if ((void*)(this->*(&Cls::toString))!=(void*)((&Cls::toString)))
{
return this->toString();
}
else
{
return "";
}
}
};
这会在GCC上编译,但是会在屏幕上显示有关从funcptr到void *的奇怪转换的警告。至少它能按预期工作。可能存在一些元编程编译时解决方案,需要考虑一下。