假设我有以下代码:
struct Z;
struct A
{
virtual void Do (Z & z) const;
};
struct B : public A {};
struct Z
{
void use (A const & a) {}
void use (B const & b) {}
};
void A::Do(Z& z) const{
z.use(*this);
}
现在,当我致电B.do
时,this
的类型为A
,这是有意义的,因为do
的实施在A
中定义}。
有没有办法调用B.do
使用use (B const &)
而无需将do
的相同代码从A
复制粘贴到B
?在我的实际代码中,我有大约15个(并且正在增长的)派生自某个基类的类,并且每次都需要复制粘贴do
的相同代码。
[编辑] 澄清:所有Do
所做的就是致电use
,没有别的。 Do
和use
是接受&访问Visitor pattern中的函数。
答案 0 :(得分:3)
既然你现在澄清了你想要的是访客模式,那么,对不起,但就是这样。 This answer显示了具有双重调度的访问者模式的工作原理。
我想到了一种使用CRTP的好方法,但根据具体情况,这可能适用于您,也可能不适合您 (注意:我使用了链接答案中的代码,因此名称不匹配,但我希望你能得到这个想法。)
// your Z
class Visitor;
// superclass needed for generic handling
struct Superbase{
virtual void Accept(Visitor& v) = 0;
};
// your A
template<class Der>
class Base : public Superbase{
public:
void Accept(Visitor& v){
v.Visit(static_cast<Der&>(*this));
}
};
// your B
class Derived1 : public Base<Derived1> {
};
// new C
class Derived2 : public Base<Derived1> {
};
class Visitor {
public:
virtual void Visit(Superbase& sup){
// generic handling of any Superbase-derived type
}
virtual void Visit(Derived1& d1){
// handle Derived1
}
virtual void Visit(Derived2& d2){
// handle Derived1
}
};
int main(){
Visitor v;
Derived1 d1;
d1.Accept(v);
}
唯一的问题:现在你错过了为任何类型A
提供通用句柄的机会,因为函数不能同时是虚拟和模板。 :| 击>
刮掉它,找到一个使用Superbase
基类的解决方案。 :)这甚至允许你有一个Superbase
的容器,并充分利用多态性。 :)
答案 1 :(得分:2)
我认为这段代码符合您的要求:
#include <iostream>
struct A;
struct B;
struct Z
{
void use (A const & a);
void use (B const & b);
};
template<typename DERIVED>
struct XX
{
void Do(Z& z){
Do(z,THIS());
}
private:
const DERIVED& THIS() const { return static_cast<const DERIVED&>(*this); }
void Do(Z& z, const DERIVED& t){
z.use(t);
}
};
struct A : public XX<A> {};
struct B : public XX<B> {};
void Z::use (A const & a) { std::cout << "use for A" << std::endl; }
void Z::use (B const & b) { std::cout << "use for B" << std::endl; }
int main(){
A a;
B b;
Z z;
a.Do(z);
b.Do(z);
return 0;
}
代码中唯一的“维护”或“样板”部分是从您自己类型模板化的模板类派生而来。
答案 2 :(得分:0)
您需要根据use
指向的类型调度this
的调用,因此您需要将另一个虚拟函数添加到A
和B
,只需调用正确的use
。我假设do
除了调用use
之外还做其他事情,否则你确实必须在每个子类中重新实现do
。它看起来像这样
struct A
{
virtual void Do (Z & z) const
{
// do stuff
use(z);
// do more stuff
}
virtual void use(Z & z) const
{
z.use(*this);
}
};
struct B : public A
{
virtual void use(Z & z) const
{
z.use(*this);
}
};
struct Z
{
void use (A const & a) {}
void use (B const & b) {}
};
答案 3 :(得分:0)
我想我必须让你失望并拒绝。这是您必须做出的权衡,以便您将类的界面分解为访问者。访问者必须知道哪个人向其报告,只要您不覆盖基类中的虚拟Do(),访问者就会将您视为A.
请有人证明我错了! (我也看到这解决了删除冗余)