如何用另一个派生类的协变返回类型实现一个函数?

时间:2016-08-18 19:42:41

标签: c++ inheritance polymorphism circular-dependency

我的程序有一个抽象类BaseNode,带有2个派生类,ChoiceNode和OpponentNode。我想在BaseNode中编写一个名为“returnOpposite”的纯虚函数,如果从ChoiceNode调用它应该返回一个OpponentNode,如果从OpponentNode调用则返回一个ChoiceNode。

在BaseNode.h中

class BaseNode {
    protected:
        virtual BaseNode& returnOpposite() = 0;
}

在ChoiceNode.h中

#include "BaseNode.h"

class ChoiceNode: public BaseNode {
    OpponentNode& returnOpposite();
}

在OpponentNode.h中

#include "BaseNode.h"

class OpponentNode: public BaseNode {
    ChoiceNode& returnOpposite();
}

我的问题是Opponent / ChoiceNode需要知道相反的类,通常可以使用前向声明来解决,但是为了让编译器能够识别出相反的类与BaseNode是协变的,它需要关于班级的背景信息。

据我了解,此信息是通过包含相应的头文件来提供的。但是,这样做会导致某种循环依赖。 ChoiceNode需要包含OpponentNode,它本身需要包含ChoiceNode,但是有了头部保护,似乎OpponentNode不会知道ChoiceNode类声明。

如何解决这个明显的捕获-22?有没有办法提供有关类的上下文信息而不涉及循环依赖?

2 个答案:

答案 0 :(得分:1)

returnOpposite()方法的所有两个可覆盖的都应具有相同的返回类型。

否则你的代码根本不会编译,这个抽象方法virtual Node& returnOpposite() = 0;在派生类中没有实现。

所以你的课程应该是这样的:

class BaseNode {
    protected:
        virtual BaseNode* returnOpposite() = 0;
}

class ChoiceNode: public BaseNode {
    virtual BaseNode* returnOpposite() override;
}

class OpponentNode: public BaseNode {
    virtual BaseNode* returnOpposite() override;
}

更新(感谢@DaveS评论):如果这两种类型都是协变的(如在草图中),那么以下内容将正常工作:

class BaseNode {
    protected:
        virtual BaseNode* returnOpposite() = 0;
}

class OpponentNode; // forward declaration

class ChoiceNode: public BaseNode {
    virtual OpponentNode* returnOpposite() override;
}

class OpponentNode: public BaseNode {
    virtual ChoiceNode* returnOpposite() override;
}

请注意,我在这里返回指向实例而不是引用的指针。

答案 1 :(得分:1)

由于您已识别出循环依赖性问题,因此您无法使用真正的变体返回类型,如您所发现的那样。

但是,您可以使用在引入协变返回类型之前有效的技术。

首先,我建议将所有类中的声明更改为virtual Node& returnOppositeImpl(),使基类纯虚拟。我还会根据类层次结构的其余部分将函数和所有实现声明为private。然后,您将在每个类returnOpposite中声明一个非虚拟保护(或公共)方法,并使用上面描述的签名。这些函数定义因为它们相互影响而不需要共变量返回类型,并且它们的实现可以放在它们各自的.cpp文件中。实现是一个简单的静态或动态转换,调用returnOppositeImpl方法。

使用阴影,您将始终调用与调用站点上的静态类型对应的returnOpposite,因此它将具有正确的引用类型。但实际的各种Node类之间的联系知识隐藏在实现中,打破了依赖循环。

所以,一起采取

在BaseNode.h中

class BaseNode {
    private:
        virtual Node& returnOppositeImpl() = 0;
    protected: // Or public:
         Node& returnOpposite() {
            return returnOppositeImpl();
         }
}

在ChoiceNode.h中

#include "BaseNode.h"
class OpponentNode;
class ChoiceNode: public BaseNode {
    private: 
        virtual Node& returnOppositeImpl();
    protected: // or public:
        OpponentNode& returnOpposite();
}

在ChoiseNode.cpp

#include "ChoiseNode.h"
#include "OpponentNode.h"
OpponentNode& ChoiseNode::returnOpposite()
{
  // You can use static cast here, if Node doesn't have virtual
  // members, and/or if you can guarantee further subclasses
  // will properly always return an OpponentNode reference.

  return dynamic_cast<OpponenentNode&>(returnOppositeImpl());
}

OpponentNode.h和OpponentNode.cpp类似于ChoiseNode实现