You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Node的Leaf与NonLeaf派生类实现可复用的SplitNode函数?

问题描述

现有抽象基类Node,派生出Leaf和NonLeaf两个子类。需实现SplitNode函数,其逻辑对两个子类几乎完全一致,仅存在以下差异:

  • Leaf操作entries向量(类型为vector<unique_ptr<IndexEntry>>),通过entries[i]->r访问矩形属性
  • NonLeaf操作children向量(类型为vector<unique_ptr<Node>>),通过children[i]->r访问矩形属性

要求避免复制粘贴重复代码,且SplitNode支持递归调用。

相关代码结构如下:

class Leaf;
class Node
{
public:
    Node();
    virtual Leaf& ChooseLeaf(const Rectangle& entry_r) = 0;   // 使Node成为抽象类
    Rectangle r;
    unique_ptr<Node> parent;
};

class Leaf : public Node
{
public:
    Leaf();
    Leaf& ChooseLeaf(const Rectangle& entry_r) override;
    vector<unique_ptr<IndexEntry>> entries;
};

class NonLeaf : public Node
{
public:
    NonLeaf();
    Leaf& ChooseLeaf(const Rectangle& entry_r) override;
    vector<unique_ptr<Node>> children;
};

SplitNode的示例逻辑:

void SplitNode()
{
    // Leaf分支:
    if (this.entries.size() > rtree.M)
    { ... } 
    
    // NonLeaf分支:
    if (children.size() > rtree.M)
    { ... }          
    
    // Leaf分支:
    entries[0]->r.DoSomething();
    
    // NonLeaf分支:
    children[0]->r.DoSomething();

    // 递归调用
    parent.SplitNode();
    
    ...
}

解决方案

推荐采用以下几种方案统一SplitNode逻辑,避免重复代码:

方案1:多态接口抽象差异(最直观)

在基类Node中定义纯虚函数,将两个子类的差异点封装为统一接口,SplitNode作为基类的非虚函数调用这些接口,实现逻辑复用。

步骤1:修改基类Node

添加抽象差异点的接口,并实现统一的SplitNode函数:

class Node
{
public:
    Node();
    virtual Leaf& ChooseLeaf(const Rectangle& entry_r) = 0;
    Rectangle r;
    unique_ptr<Node> parent;

    // 抽象差异点的接口
    virtual size_t getElementCount() const = 0;
    virtual Rectangle& getElementRect(size_t index) = 0;
    // 若需修改元素,可添加对应接口,如removeElement、addElement等

    // 统一的SplitNode函数,支持递归
    void SplitNode() {
        // 统一判断逻辑
        if (getElementCount() > rtree.M) {
            // ... 原有的分支逻辑
        }

        // 统一访问矩形属性的逻辑
        getElementRect(0).DoSomething();

        // 递归调用父节点的SplitNode,需判断parent非空避免空指针
        if (parent) {
            parent->SplitNode();
        }

        // ... 其他统一逻辑
    }
};

步骤2:子类实现接口

在Leaf和NonLeaf中实现基类定义的纯虚函数:

class Leaf : public Node
{
public:
    // ... 原有代码

    size_t getElementCount() const override {
        return entries.size();
    }

    Rectangle& getElementRect(size_t index) override {
        return entries[index]->r;
    }
};

class NonLeaf : public Node
{
public:
    // ... 原有代码

    size_t getElementCount() const override {
        return children.size();
    }

    Rectangle& getElementRect(size_t index) override {
        return children[index]->r;
    }
};

此方案符合面向对象多态特性,递归调用直接通过parent->SplitNode()实现,parent作为Node*会自动分发到对应子类的接口实现。

方案2:CRTP静态多态(消除虚函数开销)

若担心虚函数的性能开销,可采用**奇异递归模板模式(CRTP)**实现静态多态,通过模板参数推导实现差异点的复用:

步骤1:定义CRTP基类模板

class Node
{
public:
    Node();
    virtual Leaf& ChooseLeaf(const Rectangle& entry_r) = 0;
    Rectangle r;
    unique_ptr<Node> parent;

    // 定义纯虚的SplitNode,确保递归调用正常
    virtual void SplitNode() = 0;
};

template<typename Derived>
class NodeCRTP : public Node
{
public:
    using Node::Node;

    void SplitNode() override {
        Derived* derived = static_cast<Derived*>(this);

        // 统一逻辑,调用子类的具体实现
        if (derived->getElementCount() > rtree.M) {
            // ... 原有的分支逻辑
        }

        derived->getElementRect(0).DoSomething();

        if (parent) {
            parent->SplitNode();
        }

        // ... 其他统一逻辑
    }
};

步骤2:子类继承CRTP模板并实现接口

class Leaf : public NodeCRTP<Leaf>
{
public:
    Leaf();
    Leaf& ChooseLeaf(const Rectangle& entry_r) override;
    vector<unique_ptr<IndexEntry>> entries;

    size_t getElementCount() const {
        return entries.size();
    }

    Rectangle& getElementRect(size_t index) {
        return entries[index]->r;
    }
};

class NonLeaf : public NodeCRTP<NonLeaf>
{
public:
    NonLeaf();
    Leaf& ChooseLeaf(const Rectangle& entry_r) override;
    vector<unique_ptr<Node>> children;

    size_t getElementCount() const {
        return children.size();
    }

    Rectangle& getElementRect(size_t index) {
        return children[index]->r;
    }
};

此方案通过静态类型推导避免了虚函数的运行时开销,同时保证递归调用的正确性。

扩展说明

若SplitNode中还有其他差异点(如创建新节点、拆分后元素分配等),可同样将这些逻辑封装为接口(虚函数或CRTP方法),进一步复用代码。


内容的提问来源于stack exchange,提问作者dimitris93

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 21:31:17