C ++中的多态性和类抽象示例

时间:2014-10-29 19:44:20

标签: c++ delegates polymorphism abstract-class

尝试使用C ++中的多态性来解决问题。下面是两个基本的抽象类DuckFlyingBehavior,以及一系列继承的类(这是基于 Head First Design Patterns 的第一章)。

// Define an abstract fly behaviour class
class FlyBehavior {
public:
    virtual void fly() { cout << "No Flying Set!" << endl; }
};

class FlySwoop : public FlyBehavior {
public:
    void fly() { cout << "Swoop Flying!" << endl; }
};

class CantFly : public FlyBehavior {
public:
    void fly() { cout << "Can't Fly!" << endl; }
};

// Define an abstract Duck class
class Duck {
    CantFly nf;
    FlyBehavior *flyBehavior;
public:     
    Duck() { flyBehavior = &nf; }
    void goFly() { flyBehavior->fly(); }
    void setFlyBehavior(FlyBehavior *fb) { flyBehavior = fb; }
};

// Define a new Mallard Duck class
class MallardDuck : public Duck {
    FlySwoop fb;
public:
    MallardDuck(){ setFlyBehavior(&fb); }
};

// Define a new Rubber Duck class
class RubberDuck : public Duck {
    CantFly fb;
public:
    RubberDuck(){ setFlyBehavior(&fb); }
};

// Define a new Toilet Duck Class
class ToiletDuck : public Duck {};

int main(void) {

    Duck *p;
    MallardDuck mallardDuck;
    RubberDuck rubberDuck;
    ToiletDuck toiletDuck;
    p = &mallardDuck;
    p->goFly();
    p = &rubberDuck;
    p->goFly();
    p = &toiletDuck;
    p->goFly();

}

使用上面的代码我得到以下输出

Swoop Flying!
Can't Fly!
Can't Fly!

当我期待

Swoop Flying!
Can't Fly!
No Flying Set!

我是否以正确的方式接近这个例子(用Java做)?无法帮助,但我觉得我错过了一些基本的东西。我试图了解你如何从类中提取行为,将其放入另一个类,然后使用多态来委托正确的行为。有没有更好的方法来解决上述问题?

嗯,也许这就是你使用多重继承的地方?

2 个答案:

答案 0 :(得分:2)

如果你改变了

class Duck {
    CantFly nf;
    FlyBehavior *flyBehavior;
public:     
    Duck() { flyBehavior = &nf; }
    void goFly() { flyBehavior->fly(); }
    void setFlyBehavior(FlyBehavior *fb) { flyBehavior = fb; }
};

class Duck {
    FlyBehavior nf;
    FlyBehavior *flyBehavior;
public:     
    Duck() { flyBehavior = &nf; }
    void goFly() { flyBehavior->fly(); }
    void setFlyBehavior(FlyBehavior *fb) { flyBehavior = fb; }
};

您的代码将产生所需的输出。

在当前版本的代码ToiletDuck中,编译器生成的默认构造函数调用Duck类的默认构造函数,该构造函数将nf设置为指向{{1}的实例}类。当然,它会打印CantFly

答案 1 :(得分:1)

首先,您的代码具有无抽象基础类。抽象基类是具有纯virtual成员函数的基类(例如virtual fly() const=0)。 FlyBehavior是一个多态类,但不是抽象类,因为它的虚函数不是纯虚函数。 Duck甚至不是多态类(没有虚拟成员方法)。

其次,任何多态类都应该有一个virtual析构函数,这样就可以从指向多态基的指针中删除派生类型的任何对象。

接下来,派生的ducks具有比实际使用的更多的数据成员。例如,MallardDuck有一个CantFly,一个FlySwoop和一个FlyBehaviour*。这可以通过在堆上分配实际的FlyBehaviour并通过智能指针进行管理来避免。 (这可能不是这个简单示例的问题,但只要这些对象变大,就会成为一个问题。)

最后,成员函数setFlyBehavior()向公众公开,允许用户更改FlyBehavior - 您真的想要吗?

可能的设计如下

struct FlyBehavior    // polymorphic class
{
  virtual void fly() const { cout << "No Flying Set!" << endl; }
  virtual~FlyBehavior() {}
};

struct FlySwoop : FlyBehavior
{
  void fly() const { cout << "Swoop Flying!" << endl; }
};

struct CantFly : FlyBehavior
{
  void fly() const { cout << "Can't Fly!" << endl; }
};

class Duck        // non-polymorphic, but using polymorphism through member
{
  std::unique_ptr<FlyBehavior> flyBehavior; // calls FlyBehavior::~FlyBehavior at destruction
protected:
  explicit Duck(FlyBehavior*f) : flyBehavior(f) { assert(f); }
public:     
  Duck() : flyBehavior(new FlyBehavior) {}  // note: not CantFly as in your code
  Duck(Duck&&) = default;                   // allow move (but no copy)
  Duck&operator=(Duck&&) = default;
  void goFly() const { flyBehavior->fly(); }
};

struct MallardDuck : Duck
{
  MallardDuck() : Duck(new FlySwoop) {}
};

struct RubberDuck : Duck
{
  RubberDuck() : Duck(new CantFly) {}
};

FlyBehavior通常最好是抽象的。在这种情况下,Duck只能使用protected构造函数(除了移动和复制)来实现:

struct FlyBehavior    // polymorphic class
{
  virtual void fly() const=0 ;   // pure virtual
  virtual~FlyBehavior() {}
};

class Duck        // non-polymorphic, but using polymorphism through member
{
  std::unique_ptr<FlyBehavior> flyBehavior; // calls FlyBehavior::~FlyBehavior at destruction
protected:
  explicit Duck(FlyBehavior*f) : flyBehavior(f) { assert(f); }
public:
  Duck() = delete;                          // no default constructor
  Duck(Duck&&) = default;                   // allow move (but no copy)
  Duck&operator=(Duck&&) = default;
  void goFly() const { flyBehavior->fly(); }
};

以及上面的其余代码。不同之处在于您无法创建Duck对象,而只能创建一个派生的Duck类型。这两种设计中哪一种最适合取决于应用。