多态,访问者,如何调用重载函数?

时间:2018-07-11 19:37:03

标签: c++ visitor

我想创建子类对象,该子类对象将与其他子类对象发生不同的反应(类可以欢迎其他类,但不是全部) 该代码的原理是从访客设计模式派生的:

class A
{
public :
    virtual bool isWelcoming(const A* other) const =0;
    virtual bool isWelcomable(const A* other) const =0;

};

class C;

class B: public A
{
public:

    virtual bool isWelcoming(const A* other) const 
    {
        return other->isWelcomable(this);
    }

    bool isWelcomable(const A* other) const 
    {
        return false;
    }

    bool isWelcomable(const B* other) const 
    {
        return true;
    }

    bool isWelcomable(const C* other) const 
    {
        return false;
    }

};

class C: public A
{
public:

    virtual bool isWelcoming(const A* other) const 
    {
        return other->isWelcomable(this);
    }

    bool isWelcomable(const A* other) const 
    {
        return false;
    }

    bool isWelcomable(const B* other) const 
    {
        return true;
    }

    bool isWelcomable(const C* other) const 
    {
        return true;
    }
};

但是当我这样做

A *b1=new B;
A *b2=new B;
A *c=new C;

std::cout<<b1->isWelcoming(b2); //--> return false but I want true;
std::cout<<c->isWelcoming(b1); //--> return false OK;
std::cout<<b1->isWelcoming(c); //--> return false but I want true;

总是调用isWelcomable(const A * other)函数,而不是重载函数isWelcomable(const B * other)或isWelcomable(const C * other)。

这是正常的,因为A :: isWelcomable(其他常量B *)和A :: isWelcomable(其他常量C *)不存在……我不希望它们存在。

是否可以调用isWelcomable(其他B常量)或isWelcomable(其他C *常量)函数?

使用dynamic_cast但不是很干净吗?像这样:

class C;    
class B: public A
    {
public:

    virtual bool isWelcoming(const A* other) const 
    {
        return other->isWelcomable(this);
    }

    bool isWelcomable(const A* other) const 
    {
        const B* b=dynamic_cast<B*>(A);
        if (b)
            return isWelcomable(b);

        const C* c=dynamic_cast<C*>(A);
        if (c)
            return isWelcomable(c);

        return false;
    }

    bool isWelcomable(const B* other) const 
    {
        return true;
    }

    bool isWelcomable(const C* other) const 
    {
        return false;
    }

};

class C: public A
{
public:

    virtual bool isWelcoming(const A* other) const 
    {
        return other->isWelcomable(this);
    }

    bool isWelcomable(const A* other) const 
    {
        const B* b=dynamic_cast<B*>(A);
        if (b)
            return isWelcomable(b);

        const C* c=dynamic_cast<C*>(A);
        if (c)
            return isWelcomable(c);

        return false;
    }

    bool isWelcomable(const B* other) const 
    {
        return true;
    }

    bool isWelcomable(const C* other) const 
    {
        return true;
    }
};

感谢您的帮助。

UPDATE: 使用dynamic_cast的解决方案看上去有点像非循环访问者(感谢TavianBarnes)可以做我想要的,但是使用了不推荐的dynamic_cast。此外,此解决方案违反了Liskov替代原则。

我曾经考虑过像Kai Guther建议的那样使用getType(),但使用字符串代替一个枚举,但是此解决方案也违反了Liskov替代原理,而且在我看来,还超出了“ dynamic_cast”解决方案。

因此,我认为没有解决方案不违反该原则,因此我选择第一个解决方案是因为我发现它更优雅,功能也不必很快(只需一个操作即可响应)用户操作),并且该应用程序并非用于嵌入式系统。

3 个答案:

答案 0 :(得分:3)

问题是您将重载重载混合在一起。

B类中,您有isWelcomable函数的三个重载,但是其中只有一个会覆盖A::isWelcomable功能:

bool isWelcomable(const A* other) const;

这就是other->isWelcomable(this)将要调用的函数。

我建议您养成在多态时使用override特殊关键字的习惯,以确保您定义的函数确实覆盖了父类函数:

bool isWelcomable(const A* other) const override;  // Correct

bool isWelcomable(const B* other) const override;  // Incorrect, compiler error

并且如前所述,B::isWelcoming函数不会返回任何内容,即使已声明这样做。当然,这将导致undefined behavior确实使所有关于行为的猜测都没有根据。

答案 1 :(得分:0)

有一些错别字:缺少';'在课程结束时。

然后您错过了virtual bool isWelcomable(const A* other) const =0;B*类型的C*重载丢失的情况。

然后您错过了从调用的函数中返回值的方法:return other->isWelcomable(this);

为什么还要将virtual bool isWelcoming(const A* other) const定义为虚拟的?没必要!

一般提示:如果要覆盖虚拟功能,请使用override。在您的情况下,如果您使用编译器,则会发出错误消息!

始终启用所有警告。然后在想知道运行时结果之前捕获丢失的return语句。

完整的固定代码:

class A;
class B;    
class C;    

class A
{
    public : 
        virtual bool isWelcoming(const A* other) const =0;
        virtual bool isWelcomable(const A* other) const =0;
        virtual bool isWelcomable(const B* other) const =0;
        virtual bool isWelcomable(const C* other) const =0; 
};

class B: public A
{           
    public:

        bool isWelcoming(const A* other) const { return other->isWelcomable(this); }

        bool isWelcomable(const A* other) const override { return false; }
        bool isWelcomable(const B* other) const override { return true; }
        bool isWelcomable(const C* other) const override { return false; }
};

class C: public A
{       
    public:

        bool isWelcoming(const A* other) const { return other->isWelcomable(this); }

        bool isWelcomable(const A* other) const override { return false; }
        bool isWelcomable(const B* other) const override { return true; }
        bool isWelcomable(const C* other) const override { return true; }
};

int main()
{
    A *b1=new B;
    A *b2=new B;
    A *c=new C;

    std::cout<< b1->isWelcoming(b2); //--> return false but I want true;
    std::cout<< c ->isWelcoming(b1); //--> return false OK;
    std::cout<< b1->isWelcoming(c); //--> return false but I want true;
}

答案 2 :(得分:0)

您传递给isWelcoming的基类指针不能用于调用函数

 bool isWelcomable(const B* other) const;
 bool isWelcomable(const C* other) const;

由于A没有具有该签名的成员。虽然可以用派生类型替换虚拟函数的返回类型,但这不适用于参数。

要获得所需的行为,A每个派生类型都需要一个成员函数isWelcomable

class A{
public :
  virtual bool isWelcoming(const A* other) const =0;
  virtual bool isWelcomable(const A* other) const =0;
  virtual bool isWelcomable(const B* other) const =0;
  virtual bool isWelcomable(const C* other) const =0;
}

更新

如果您不希望A引用派生类,则也可以将isWelcomable函数换成getType函数

// enum for convenience
enum AType{a,b,c};                                                                             

class A{                                                                                                                                                                                      
public :                                                                                       
  virtual bool isWelcoming(const A* other) const =0; 
  // tell which derived type you are                                          
  virtual AType getType() const {return a;};                                                    
};                                                                                             

class B: public A{                                                                                                                                                                       
public:                                                                                      

  virtual bool isWelcoming(const A* other) const                                             
  {                                 
    // decide based on what type other points to                                                         
    switch(other->getType()){                                                                
    case(a):                                                                                 
      return false;                                                                          
    case(b):                                                                                 
      return true;                                                                           
    case(c):                                                                                 
      return true;                                                                           
    }                                                                                        
  }                                                                                          

  AType getType() const {return b;}                                                          
};                                                                                           

class C: public A{                                                                                                                                                                   
public:                                       

  virtual bool isWelcoming(const A* other) const
  {                                                                                                                             
    switch(other->getType()){                                                              
    case(a):                                                                               
      return false;                                                                        
    case(b):                                                                               
      return false;                                                                        
    case(c):                                                                               
      return true;                                                                         
    }                                                                                      
  }                                                                                        

  AType getType() const {return c;}                                                        
};          

请注意,这会将有关B是否欢迎C等信息从访客移到主机,因此B现在必须知道它欢迎什么,而不是谁欢迎。

它也不是一个非常优雅的解决方案,实际上,派生类的列表现在已从A移至AType。由于每个派生类都需要能够根据类型对彼此派生类做出反应,因此如果没有这样的列表,就无法解决此问题。