Ошибка дампа ядра C ++ при реализации семейного дерева - PullRequest
2 голосов
/ 05 апреля 2020

Я пытаюсь реализовать генеалогическое древо. У меня есть классы Person и Tree, определенные следующим образом:

Файл FamilyTree.hpp:

using namespace std;
#include <string>

namespace family{

    class Person{
        public:
        string name;
        Person* mother;
        Person* father;

        Person(string name);

    };

    class Tree{
        public:
        Person* root;

        Tree(string name);

        Tree& addFather(string name1, string name2);
        Tree addMother(string name1, string name2);
        void display();
        string relation(string name);
        string find(string name);
        void remove(string name);
    };

};

Файл FamilyTree.cpp:

#include "FamilyTree.hpp"
#include <string>
#include <iostream>
using namespace family;   

// FUNCTIONS

Person& findPerson(Person& root, string child_name){
    if (root.name.compare(child_name) != 0)
    {
        cout<<root.name<<":1"<<endl;
        findPerson(*root.father, child_name);
    }
    else if(root.name.compare(child_name) == 0){
        cout<<root.name<<":2"<<endl;
        return root;  
    }else{
        cout<<"not found!!!"<<endl;
        Person p("no found");
        return p;
    }
}

// PERSON
family::Person::Person(string person_name){
    name = person_name;
    father = nullptr;
    mother = nullptr;
};

// TREE
family::Tree::Tree(string name){
    root = new Person(name);
};

family::Tree& Tree::addFather(string child, string father){
    Person& child_found = findPerson(*root, child);

    //cout<<"child_found.name:"<<child_found.name<<endl;
    child_found.father = new Person(father);
    return *this;
    };

family::Tree family::Tree::addMother(string name1, string name2){return Tree("");};
void family::Tree::display(){};
string family::Tree::relation(string name){return "";};
string family::Tree::find(string name){return "";};
void family::Tree::remove(string name){};

int main(){
    Tree t("X");

    t.addFather("X", "Y");
    t.addFather("Y","Z");
    return 0;
}

Я начал с функции addFather(): addFather("child", "new father") для добавления нового отца для существующего ребенка. Я реализовал его рекурсивно, используя функцию findPerson(), которая возвращает объект Person для дочернего объекта и addFather() fun c, создавая новый Person и инициализируя его для найденного дочернего элемента.

После добавления 2 отцы, я получаю ошибку "Недопустимая инструкция (core dumped)", в чем проблема?

Ответы [ 3 ]

2 голосов
/ 05 апреля 2020

Если вы включите свои предупреждения, вы увидите, что вы не всегда возвращаетесь с findPerson. В этой функции

Person& findPerson(Person& root, string child_name){
    if (root.name.compare(child_name) != 0)
    {
        cout<<root.name<<":1"<<endl;
        findPerson(*root.father, child_name);      // (1)
    }
    else if(root.name.compare(child_name) == 0){
        cout<<root.name<<":2"<<endl;
        return root;  
    }else{
        cout<<"not found!!!"<<endl;
        Person p("no found");
        return p;                      // (2)
    }
}

Код в первом if -ветвлении, отмеченном (1), должен возвращать рекурсивно найденного персонажа, например:

return findPerson(*root.father, child_name);

. позаботьтесь о segfault.

Однако в последней ветви этой функции есть более глубокая проблема, когда вы возвращаете ссылку на локальную переменную p, помеченную (2). Если вы сделаете это, вы вернете висячую ссылку, поскольку p будет go выходить из области действия при возврате функции.

Вам нужно подумать о том, что должна делать эта функция, если не найдено Person.

  • Возможно, вы можете вернуть Person*, поэтому nullptr означает, что никого не найдено.

  • Вы можете вернуть std::optional<Person> ,

1 голос
/ 05 апреля 2020

Проблема в том, что вы не можете вернуть ссылку на локальный объект. Это приведет к неопределенному поведению.

Когда вы добавляете новый член, findPerson() не найдет попытки вернуть такой локальный объект p. Это обречено на провал.

Этот дизайн не является оптимальным. Возможные решения:

  • объявляет статический c объект p, который возвращается, когда ничего не найдено. В отличие от местных жителей, stati c остается живым, и возвращаемая ссылка остается в силе. Однако вы должны убедиться, что эта возвращаемая ссылка никогда не используется таким образом, чтобы это могло изменить имя вашего объекта c.
  • изменить интерфейс findPerson(), чтобы он возвращал указатели. Либо он возвращает действительный указатель на человека, либо он возвращает nullptr, если ничего не найдено. Это распространенная идиома (при разработке в реальном мире вы бы возвращали итератор, но если вы начинаете в C ++, это пока будет слишком сложно).
  • бросить исключение, если ничего не найдено, и переписать ваш вызывающий код, чтобы он перехватил исключение. Я бы не советовал использовать этот подход здесь: исключения лучше хранить для действительно исключительных ситуаций.

Обратите внимание, что я не заглядывал в ваш код, если были другие ошибки. Я просто остановился на первой попытке найти что-то, чего не существует, когда дерево пусто.

0 голосов
/ 07 апреля 2020

Это работает для поиска отцов, но мне нужно, чтобы оно перебирало все дерево. Когда я добавил матерей root, это дамп памяти снова.

Person& findPerson(Person& root, string child_name){
    if (root.name.compare(child_name) != 0)
    {
        //cout<<root.name<<":1"<<endl;
        return findPerson(*root.father, child_name); 
        return findPerson(*root.mother, child_name);     // (1)
    }
    else if(root.name.compare(child_name) == 0){
        //cout<<root.name<<":2"<<endl;
        return root;  
    }else{
        throw exception();                     
    }
}
...