【问题标题】:can i restrict behaviour of polymorphism?我可以限制多态性的行为吗?
【发布时间】:2021-08-01 12:47:27
【问题描述】:

所以我正在尝试实现二叉搜索树和 avl 树。这些类中的每一个都使用不同但相似类型的节点。节点的类如下所示:

class node
{
protected:
    int key;
    node* parent, * left, * right;
public:
    node(int key, node* parent = nullptr) :key(key), parent(parent), left(nullptr), right(nullptr) {}
    ~node() {}
};

class avl_node : public node
{
private:
    int height;
public:
    avl_node(int key, int height, avl_node* parent = nullptr) :node(key, parent), height(height) {}
    ~avl_node() {}
};

这主要是有效的。任何node 可以与任何其他node 建立联系,任何avl_node 可以与任何其他avl_node 建立联系。我想到的问题是,由于多态性,node 在技术上可能有一个或多个父级或子级avl_node,我不希望这种情况发生。虽然我可以通过小心来避免这种情况,但我根本不希望它成为可能。有没有办法?
p.s.我想保持类相关

【问题讨论】:

  • 如果你不想要多态,那为什么还要使用子类呢?您能否详细说明您的代码有什么问题?你想达到什么目标?
  • 如果你想要容器的类型安全实现,你应该研究模板,而不是多态——这正是它们被发明的原因。否则,您将与该语言的核心功能作斗争。
  • 你不能既保持类的相关性又避免多态性。为什么要让它们保持关联?
  • @molbdnilo 避免重复代码
  • @stack_overflow_nickname 你不需要相关的类。

标签: c++ class inheritance polymorphism nodes


【解决方案1】:

如果足够,您可以显式删除将采用 avl_node* 的构造函数版本

class node
{
protected:
    int key;
    node* parent, * left, * right;
public:
    node(int key, node* parent = nullptr) :key(key), parent(parent), left(nullptr), right(nullptr) {}
    node(int, avl_node*) = delete;
    ~node() {}
};

当然,这个解决方案并非万无一失。如果您将 avl_node 隐藏在 node 指针后面,编译器将无法分辨(并且由于多态性主要是动态的,因此只有在尝试直接分配指针的特定情况下才会受到保护)

这会编译。

avl_node myavl;
node n(0, static_cast<node*>(&myavl));

您可以尝试在 node 构造函数中进行动态转换,以判断它是否是正在传递的 avl_node(您的节点需要一个 vtable),但这将使得无法从avl_node构造函数。

另一种选择是为子类创建一个单独的构造函数

class node
{
protected:
    int key;
    node* parent, * left, * right;
    node(int key, avl_node* parent) : key(key), parent(parent), left(nullptr), right(nullptr) {}
public:
    node(int key, node* parent = nullptr) :key(key), parent(parent), left(nullptr), right(nullptr) { /* do something to not allow avl_node* to be passed */ }
    ~node() {}
};

【讨论】:

  • 我这里有个问题。由于avl_node 是从node 派生的,因此在'node' 类内部,甚至没有定义avl_node。我尝试包含您建议的以下行node(int, avl_node*) = delete;,但出现此错误:syntax error: identifier 'avl_node'
  • 只需向前声明您需要的内容(或 node(int, class avl_node*)class 关键字已经算作声明)
【解决方案2】:

您可以摆脱多态性使用类模板作为基类模板和依赖于模板参数的成员。

例子:

template<typename T>
class node
{
protected:
    int key;
    node* parent, * left, * right;  // Note: these are actually 'node<T>'.
public:
    node(int key, node* parent = nullptr) 
        : key(key), parent(parent), left(nullptr), right(nullptr) {}
    void set_parent(node* p) { parent = p; }
};

class avl_node : public node<avl_node>
{
private:
    int height;
public:
    avl_node(int key, int height, avl_node* parent = nullptr) 
        : node(key, parent), height(height) {}
};

class silly_node : public node<silly_node>
{
public:
    silly_node() : node(0) {} 
};

int main()
{
    // Fine
    avl_node an(0, 1);
    // Fine
    avl_node bn(0, 1, &an);
    // Also fine
    bn.set_parent(&an);
    // Fine
    silly_node sn;
    // Compilation error
    avl_node cn(0, 1, &sn);
    // Also compilation error
    bn.set_parent(&sn);
}

【讨论】:

  • 为什么node实际上是node&lt;T&gt;?这是如何运作的?另外,我不清楚如何创建node 类的实例。
  • 在模板内部,模板名称本身是指当前的实例化。而你不能创建 node 实例是重点; node 实例化不相关,但从基本模板的实例化继承代码。
  • 所以我的想法是在我的binary_search_tree 类中使用silly_node 并在我的avl_tree 类中使用avl_node
  • silly_node 只是说明编译错误的示例。顾名思义,它很愚蠢,不应该用于任何事情。这个想法是为每种类型的树创建一个实例化 - 例如class binary_search_node : public node&lt;binary_search_node&gt; ...
  • 我在这里又遇到了一个问题。所以我创建了一个binary_search_tree 类,它有一个成员root,它的类型是node*。在我尝试做root=root-&gt;left 的方法中,但我收到以下错误:t_node&lt;node&gt;* 类型的值不能分配给node* 类型的实体。附: t_node 是模板类,node 是从它派生的类
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多