为什么不能将多态派生 class 嵌套在基础 class 中?

Why can't polymorphic derived classes be nested inside the base class?

例如,我尝试做这样的事情:

class Animal {
public:
    virtual const char *says() const = 0;

    static Animal *makeLion() { return new Lion(); }
    static Animal *makeTiger() { return new Tiger(); }
    static Animal *makePig() { return new Pig(); }

private:
    class Lion : public Animal { // error: invalid use of incomplete type ‘class Animal’
    public:
        virtual const char *says() const
        {
            return "roar";
        }
    };

    class Tiger : public Animal { // error: invalid use of incomplete type ‘class Animal’
    public:
        virtual const char *says() const
        {
            return "meow";
        }
    };

    class Pig : public Animal { // error: invalid use of incomplete type ‘class Animal’
    public:
        virtual const char *says() const
        {
            return "That\'s all Folks!";
        }
    };
};

编译器抱怨 Animal 是一个不完整的类型。但是为什么 Animal 是一个不完整的类型,如果不需要内部 class 定义来定义外部 class 本身(因为内部 class 类型没有非静态变量在外部 class)?

中按值声明

是否有解决此问题的方法或更好的方法来完成我想做的事情?

Is there a way around this or a better way to do what I'm trying to do?

不要使用嵌套的 classes。只需将派生的 classes 移出 Animal.


单独说明,具有函数

static Animal *makeLion() { return new Lion(); }
static Animal *makeTiger() { return new Tiger(); }
static Animal *makePig() { return new Pig(); }

in Animal 是设计不佳的症状。基础 class 应该尽可能地与从它派生的 class 无关。


这里有一个更简洁的界面和实现的建议:

Animal.h:

namespace AnimalsNamespace
{
   // The base class
   class Animal
   {
      public:
         virtual const char *says() const = 0;
   };


   // Functions to construct objects of various sub-types of Animal.
   // Moving these out of Animal and putting them in the namespace makes
   // Animal a little bit cleaner.

   Animal* makeLion();
   Animal* makeTiger();
   Animal* makePig();
}

Animal.cpp:

namespace AnimalsNamespace
{
   class Lion : public Animal
   {
      public:
         virtual const char *says() const
         {
            return "roar";
         }
   };

   class Tiger : public Animal
   {
      public:
         virtual const char *says() const
         {
            return "meow";
         }
   };

   class Pig : public Animal
   {
      public:
         virtual const char *says() const
         {
            return "That\'s all Folks!";
         }
   };

   Animal* makeLion() { return new Lion(); }
   Animal* makeTiger() { return new Tiger(); }
   Animal* makePig() { return new Pig(); }
}

A class 在 class 定义的右大括号 } 之前是不完整的。

关于“有没有办法解决这个问题”,你可以这样做:

struct A
{
    struct B;
};

struct A::B
    : A
{};

但这不是常见的模式。我不记得曾经看过它。

一个可能的解决方案是将 Lion、Tiger、Pig 的定义放在函数范围内:

class Animal {
public:
    virtual ~Animal() = default;
    virtual const char *says() const = 0;

    static std::unique_ptr<Animal> makeLion()
    {
        class Lion : public Animal
        {
        public:
            virtual const char *says() const override
            {
                return "roar";
            }
        };
        return std::make_unique<Lion>();
    }
    static std::unique_ptr<Animal> makeTiger() {
        class Tiger : public Animal
        {
        public:
            virtual const char *says() const override
            {
                return "meow";
            }
        };
        return std::make_unique<Tiger>();
    }
    static std::unique_ptr<Animal> makePig() {
        class Pig : public Animal
        {
        public:
            virtual const char *says() const override
            {
                return "That\'s all Folks!";
            }
        };
        return std::make_unique<Pig>();
    }
};

int main() {    
    std::cout << Animal::makeLion()->says() << std::endl;
}

Demo.

糟糕的设计。制作对象不应该在 Animal class 中完成。考虑像这样重新设计 classes:

class Animal
{
public:
    virtual const char* says() const = 0;
};

class Lion : public Animal
{
public:
    virtual const char *says() const
    {
        return "roar";
    }
};

class Tiger : public Animal
{
public:
    virtual const char *says() const
    {
        return "meow";
    }
};

class Pig : public Animal
{
public:
    virtual const char *says() const
    {
        return "That\'s all Folks!";
    }
};

然后像这样使用:

Animal* p = new Pig;
cout << p->says() << endl;

否则,每次添加新种类时都必须添加 makeXXX() 函数。