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

树节点插入与修改:如何确保函数调用间变更持久化?

家族树节点修改无法持久化的问题解决

我正在实现存储自定义Patient对象的Family_tree容器,但编写插入及相关函数时遇到问题——对节点的修改无法在树中持久化。

需求是:GUI用户选中一个Patient后,点击“Add spouse”或“Add child”按钮,对应的槽函数调用insert函数,传入两个目标Patient及二者关系,完成节点关联。

当前设计思路:

  • insert函数(已注释)通过insertHelper查找树中已存在的节点并返回,若不存在则创建新节点;
  • 随后调用insertNodes根据关系参数调整两个节点间的关联。

尝试过用node*&引用,但因为节点是局部变量创建的,即便GUI的PatientRelatives(甚至MainWindow)类持有Family_tree成员,修改还是无法持久化。


相关代码

Family_tree.h

class Family_tree {

private:
class node {

public:
Patient& p;
node* spouse;
node* parent;
std::vector<node*> children;
mutable bool visited;
static Patient default_patient;
node() : p(default_patient), spouse(nullptr), parent(nullptr), children(), visited(false) {}
node(Patient& ptn, node* s, node* p, std::vector<node*> c) : node(ptn, s, p, c, false) {}
node(Patient& ptn, node* s, node* p, std::vector<node*> c, bool v) : p(ptn), spouse(s), parent(p), children(c), visited(v) {}

};

node* root;
};

Family_tree.cpp

Family_tree::node* Family_tree::find(Family_tree::node* current_node, const Patient& p) const {
    if (p.valid_Patient()) {
        if (current_node == nullptr || current_node->visited) {return nullptr;}
        current_node->visited = true;
        if (current_node->p == p) {current_node->visited = false; return current_node;}
        for (node* child : current_node->children) {
            node* n = find(child, p);
            if (n != nullptr) {current_node->visited = false; return n;}
        }
        current_node->visited = false;
        return nullptr;
    }
    else {std::cerr << "Please input a valid patient." << std::endl; return nullptr;}
}

Family_tree::node* Family_tree::insertHelper(Patient& p) {
    node* n = find(root, p);
    if (n == nullptr) {n = new node(p, nullptr, nullptr, std::vector<node*>());}
    return n;
}

void Family_tree::insertNodes(node*& node1, node*& node2, const std::string& relationship) {
    if (relationship == "spouse") {
        if (node1->p.get_Sex() != node2->p.get_Sex()) {
        node1->spouse = node2;
        node2->spouse = node1;
        }
        else {throw UnrealisticGender();}
    }
    else if (relationship == "parent") {
        insertNodes(node2, node1, "child");
    }
    else if (relationship == "child") {
        if (node2->spouse != nullptr) {
            std::vector<Patient*> possible_children = node2->p.inheritance(node2->spouse->p);
            bool realistic_genotype = false;
            for (Patient* child : possible_children) {
                if (child->get_Genotype() == node1->p.get_Genotype()) {
                    realistic_genotype = true;
                    break;
                }
            }
            if (realistic_genotype) {
                node2->children.push_back(node1);
                node1->parent = node2;
                check_duplicates(node2);
            }
            else {throw UnrealisticGenotype();}
        }
        else {throw SpouseMissing();}
    }
    else {std::cerr << "Insert was not set up correctly." << std::endl;}
}

/*void Family_tree::insert(Patient& p1, Patient& p2, const std::string& relationship) {
    node* node1 = insertHelper(p1);
    node* node2 = insertHelper(p2);
    insertNodes(node1, node2, relationship);
}*/

PatientRelatives.h(GUI模块)

class PatientRelatives : public QWidget {
    Q_OBJECT

private:
    Family_tree& family;
    SelectedPatient& selectedPatient;

public:
    PatientRelatives(Family_tree& family, SelectedPatient& selectedPatient, QWidget* parent = 0);
    void show();

private slots:
    void addParents();
    void addSpouse();
    void addChild();
    //void addSibling();
    void changeRelatives();
};

PatientRelatives.cpp(GUI模块)

void PatientRelatives::addSpouse() {
    Patient patient = Patient();
    Patient* selected = selectedPatient.getSelectedPatient();
    EditDetailsDialog* spouseDialog = new EditDetailsDialog(patient, this);
    if (spouseDialog->exec() == QDialog::Accepted) {
        Patient spouse = spouseDialog->getPatient();
        try {
            family.insert(spouse, *selected, "spouse");
        } catch(const UnrealisticGender& e) {
            QMessageBox::critical(this, "Error", e.what());
            family.delete_p(spouse);
        }
        delete spouseDialog;
    }
}

void PatientRelatives::addChild() {
    Patient patient;
    Patient* selected = selectedPatient.getSelectedPatient();
    EditDetailsDialog* childDialog = new EditDetailsDialog(patient, this);
    if (childDialog->exec() == QDialog::Accepted) {
        Patient child = childDialog->getPatient();
        try {
            family.insert(child, *selected, "child");
        } catch (const SpouseMissing& e) {
            QMessageBox::critical(this, "Error", e.what());
            addSpouse();
            try {
                family.insert(child, *selected, "child");
            } catch (const UnrealisticGenotype& e) {
                QMessageBox::critical(this, "Error", e.what());
                family.delete_p(child);
            }
        } catch (const UnrealisticGenotype& e) {
            QMessageBox::critical(this, "Error", e.what());
            family.delete_p(child);
        }
        delete childDialog;
    }
}

问题分析与修复方案

核心问题

  1. 新节点未挂载到树结构:insertHelper创建新节点后,仅返回指针但未将其关联到家族树的任何现有节点(如根节点、选中节点),导致新节点游离,后续无法被找到,修改自然无法持久化。
  2. Patient引用悬空:node类用Patient& p存储对象,但GUI中创建的Patient是局部对象,函数结束后局部对象销毁,引用变为悬空状态,触发未定义行为。
  3. find函数遍历不完整:仅遍历子节点,未处理配偶节点,导致无法找到树中已存在的配偶;同时visited标记逻辑可能因循环引用(如配偶关系)导致遍历中断。

修复步骤

1. 修复节点挂载逻辑

修改insert函数,确保新创建的节点被纳入树结构,处理根节点为空的情况:

void Family_tree::insert(Patient& p1, Patient& p2, const std::string& relationship) {
    node* node1 = insertHelper(p1);
    node* node2 = insertHelper(p2);
    
    // 树为空时,将第一个节点设为根
    if (root == nullptr) {
        root = node1;
    }
    
    insertNodes(node1, node2, relationship);
}

2. 替换Patient引用为值或智能指针

避免悬空引用,将node类中的Patient& p改为值存储或智能指针:

class node {
public:
    Patient p; // 改为值存储,或使用std::shared_ptr<Patient>管理生命周期
    node* spouse;
    node* parent;
    std::vector<node*> children;
    mutable bool visited;
    static Patient default_patient;
    // 构造函数同步修改
    node() : p(default_patient), spouse(nullptr), parent(nullptr), children(), visited(false) {}
    node(Patient ptn, node* s, node* p, std::vector<node*> c) : node(ptn, s, p, c, false) {}
    node(Patient ptn, node* s, node* p, std::vector<node*> c, bool v) : p(ptn), spouse(s), parent(p), children(c), visited(v) {}
};

3. 完善find函数遍历逻辑

增加配偶节点的遍历,优化visited标记避免循环:

Family_tree::node* Family_tree::find(Family_tree::node* current_node, const Patient& p) const {
    if (!p.valid_Patient()) {
        std::cerr << "Please input a valid patient." << std::endl;
        return nullptr;
    }
    if (current_node == nullptr || current_node->visited) {
        return nullptr;
    }
    current_node->visited = true;
    
    if (current_node->p == p) {
        current_node->visited = false;
        return current_node;
    }
    
    // 遍历子节点
    for (node* child : current_node->children) {
        node* n = find(child, p);
        if (n != nullptr) {
            current_node->visited = false;
            return n;
        }
    }
    
    // 遍历配偶节点
    if (current_node->spouse != nullptr) {
        node* n = find(current_node->spouse, p);
        if (n != nullptr) {
            current_node->visited = false;
            return n;
        }
    }
    
    current_node->visited = false;
    return nullptr;
}

4. 调整GUI中Patient对象的生命周期

避免局部对象销毁导致的问题,改用动态分配:

void PatientRelatives::addSpouse() {
    Patient* patient = new Patient();
    Patient* selected = selectedPatient.getSelectedPatient();
    EditDetailsDialog* spouseDialog = new EditDetailsDialog(*patient, this);
    if (spouseDialog->exec() == QDialog::Accepted) {
        *patient = spouseDialog->getPatient();
        try {
            family.insert(*patient, *selected, "spouse");
        } catch(const UnrealisticGender& e) {
            QMessageBox::critical(this, "Error", e.what());
            family.delete_p(*patient);
            delete patient;
        }
    } else {
        delete patient; // 用户取消时释放内存
    }
    delete spouseDialog;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 05:16:24