【问题标题】:Why can't polymorphic derived classes be nested inside the base class?为什么多态派生类不能嵌套在基类中?
【发布时间】:2016-12-12 18:16:16
【问题描述】:

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

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 是一个不完整的类型?

有没有办法解决这个问题或有更好的方法来做我想做的事情?

【问题讨论】:

  • 一堂课在你到达结束};之前是不完整的。
  • 课程在完成之前是不完整的。您尝试这样做的具体动机是什么?
  • @NathanOliver:这并不完全正确。您可以在Animal 中声明Animal 的静态成员。据我所知,内部类被视为静态的。
  • @Matt 这是一个特例。它仍然是一个不完整的类型,直到完成后在类之外完成的定义。在看到}; 之前,一个类本身永远不会被认为是完整的。
  • @ChristianHackl:嗯,我认为constexpr static 数据成员的定义算作定义。这件事一团糟。我一直不明白要求和限制的原因。

标签: c++ inheritance polymorphism inner-classes


【解决方案1】:

在类定义的右大括号} 之前,类才算完整。

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

struct A
{
    struct B;
};

struct A::B
    : A
{};

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

【讨论】:

  • 谢谢,这正是我想要的(因为struct B 可以成为struct A 的私人成员)!
【解决方案2】:

有没有办法解决这个问题或有更好的方法来做我想做的事情?

不要使用嵌套类。只需将派生类移出Animal


单独说明,具有功能

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

in 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(); }
}

【讨论】:

  • 我希望这些课程是私有的。另外,听说过工厂模式吗?基类没有理由不能成为其有限的派生类集的工厂。
  • 当您说私有时,您的意思是对用户隐藏吗?
  • 嗯,我希望它们被有效地隐藏,而不必做任何古怪的事情。
  • @Matt 将它们放入 .cpp 文件中。
  • @jaggedSpire:存在关注点过度分离这样的事情。另外,它们不是静态的吗?
【解决方案3】:

一种可能的解决方案是将 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.

【讨论】:

  • 我考虑过,但是任何其他派生类特性都不能从基类访问。而且你不能为同一个类创建多个工厂方法。
【解决方案4】:

糟糕的设计。制作对象不应该在 Animal 类中完成。考虑重新设计类,例如:

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() 函数。

【讨论】:

  • 如果那是我想做的,我会那样做的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-02
  • 1970-01-01
  • 2016-02-14
相关资源
最近更新 更多