当前位置: 首页 > 知识库问答 >
问题:

具有扩展接口的派生类集合。如何在没有动态强制转换的情况下访问派生接口?

万承志
2023-03-14

假设我有一个“Animal”类型的抽象对象。动物有公共纯虚拟的方法“吃”。

我想把动物派生成一个“狗”和“猫”,每个都有一个扩展接口。例如,我希望狗有一个公共方法“chaseTail”,猫有一个公共方法“销毁家具”。

我想在一个“世界”对象中收集动物。

我需要能够使用“GetAnimalPosition”方法从世界上检索这些动物,并能够任意调用chaseTail或销毁我得到的动物上的家具。

我想避免缓慢的dynamic\u投射,测试位置是否是给定的动物,或者将追逐尾巴吊起来并将家具破坏成动物,但我似乎回到了一个角落。

还有别的办法吗?

共有3个答案

仇浩旷
2023-03-14

希望在运行时发现多态层次结构的动态类型通常表明存在设计错误。您可以尝试以下方式:

struct Animal
{
    virtual ~Animal()             {              }
    void eat()                    { doEat();     }
    void performTypicalActivity() { doTypical(); }
private:
    virtual void doEat() = 0;
    virtual void doTypical() = 0;
};

struct Cat : Animal
{
    void destroyFurniture();
    void purr();
private:
    virtual void doTypical() { destroyFurniture(); purr(); }
};

现在您遍历您的集合并调用p-

丘友樵
2023-03-14

您可以在完全了解所有类的情况下执行访问者模式操作。

岳池暝
2023-03-14

访问者模式是一个可行的解决方案。该解决方案有两个主要参与者:

  • 元素:具有接受访问者的公共父级的不同类型。在这种情况下,元素是猫和狗,共同的父母是动物

对于本例,请从元素(动物、猫和狗)开始:

class Animal
{
public:
  virtual ~Animal() {}
  virtual void eat() = 0;
};

class Cat: public Animal
{
public:
  void destroyFurniture();
  void eat(); 
};

class Dog: public Animal
{
public:
  void chaseTail();
  void eat();
};

接下来,创建一个将“访问”每个元素的访问者。访问者将知道它正在操作的类型,因此可以在这两个特定元素上使用方法,例如,Cat::destroyFurniture()和Dog::chaseTail():

class Visitor
{
public:
   void visitDog( Dog& dog ) { dog.chaseTail();        }
   void visitCat( Cat& cat ) { cat.destroyFurniture(); }
};

现在,向动物添加一个纯虚拟方法,该方法接受访问者作为参数:ulual动物::accept(Vistor

class Animal
{
public:
  ...
  virtual void accept( Visitor& ) = 0;
};

class Cat: public Animal
{
public:
  ...
  virtual void accept( Visitor& visitor ) { visitor.visitCat( *this ); }
};

请注意如何使用虚方法来解析特定的元素类型,以及每个元素的accept实现将调用访问者上的方法。这允许执行基于类型的分支,而无需使用dynamic\u cast,通常称为双重调度。

下面是一个可编译的示例,演示了正在使用的模式:

#include <iostream>
#include <vector>

using std::cout;
using std::endl;

class Cat;
class Dog;

class Visitor
{
public:
   void visitCat( Cat& cat );
   void visitDog( Dog& dog );
};

class Animal
{
public:
  virtual ~Animal() {}
  virtual void eat() = 0;
  virtual void accept( Visitor& ) = 0;
};

class Cat: public Animal
{
public:
  void destroyFurniture()         { cout << "Cat::destroyFurniture()" << endl; }
  void eat()                      { cout << "Cat::eat()" << endl;              }  
  void accept( Visitor& visitor ) { visitor.visitCat( *this );                 }
};

class Dog: public Animal
{
public:
  void chaseTail()                { cout << "Dog::chaseTail()" << endl; }
  void eat()                      { cout << "Dog::eat()" << endl;       }  
  void accept( Visitor& visitor ) { visitor.visitDog( *this );          }
};

// Define Visitor::visit methods.
void Visitor::visitCat( Cat& cat ) { cat.destroyFurniture(); }
void Visitor::visitDog( Dog& dog ) { dog.chaseTail();        }

int main()
{
  typedef std::vector< Animal* > Animals;
  Animals animals;
  animals.push_back( new Cat() );
  animals.push_back( new Dog() );

  Visitor visitor;  
  for ( Animals::iterator iterator = animals.begin();
        iterator != animals.end(); ++iterator )
  {
    Animal* animal = *iterator;
    // Perform operation on base class.
    animal->eat();
    // Perform specific operation based on concrete class.
    animal->accept( visitor );
  }

  return 0;
}

产生以下输出:

Cat::eat()
Cat::destroyFurniture()
Dog::eat()
Dog::chaseTail()

请注意,在此示例中,Visitor是一个具体类。但是,可以为Visitor创建整个层次结构,允许您根据Visitor执行不同的操作。

class Visitor
{
public:
   virtual void visitCat( Cat& ) = 0;
   virtual void visitDog( Dog& ) = 0; 
};

class FurnitureDestroyingVisitor: public Visitor
{
   virtual void visitCat( Cat& cat ) { cat.destroyFurniture(); }
   virtual void visitDog( Dog& dog ) {} // Dogs cannot destroy furniture.
};

访问者模式的一个主要缺点是,添加元素可能需要更改访问者类。一般的经验法则是:

  • 如果Element层次结构可能会发生变化,那么在基本Element类上定义操作可能会更容易。在本例中,如果需要添加CowPig,那么将虚拟do典型方法添加到动物可能会更容易。
  • 如果Element层次结构是稳定的,但在Elements上运行的算法正在发生变化,那么Visitor模式可能是一个很好的候选者。

 类似资料:
  • 问题内容: 当然,这个问题可能不是树莓派所特有的。另外,我对Linux还比较陌生。 我想编写一个小库(在node.js中,如果有关系的话),以使用sysfs访问树莓派的GPIO。但是,访问sysfs需要sudo访问,这很明显是有原因的。 Quick2Wire似乎有解决方案,但我想更好地理解它,而不仅仅是盲目使用。他们当然使用了C,但是据我了解,代码并不复杂,即使不那么优雅,也可以仅使用bash即可

  • 问题内容: 返回所有直接实现的接口,即不遍历类树以获取所有父类型的所有接口。例如,层次结构 对于我想得到,但对于任何任意的树深度。 我可以自己写这个,但是我确定必须有一个已经存在的库,有什么想法吗? 问题答案: Apache Commons Lang 具有您需要的方法:

  • 我正在学习一本书,即“.NET域驱动的C#设计”。 问题基于如下类图所示的场景: 图:http://screencast.com/t/a9uuljvw0 现在,如果我用ICompanyRepository变量comrep调用Add()函数... 然后调用RepositoryBase类(它是CompanyRepository的父类)中的Add()函数。 我的问题是:在(抽象基)类“repositor

  • return在没有强制转换的情况下从整数创建指针->return*b; 赋值从没有强制转换的指针生成整数->**trans_matrix=转置(矩阵[0],trans_matrix[0],r,c);警告。 如何修复代码未显示警告?

  • 我想查看项目中的所有类,当我找到一个从“City”派生的类时,我想创建一个该类型的新对象并将其添加到列表中。这允许我添加功能,而无需更新列表。最重要的是,我想在不使用任何库的情况下完成它。我已经找到了类似的主题,但他们使用了org。反思。我希望避免以下情况: