Help with family tree program

Hi there, I'm still working on the same family tree program, but I'm having trouble with implementing the addWife and addChild function. Ideally, I want the final output to display as ...

Child of Abraham
1. Sarah
.... Issac
2. Keturah
.... Jokshan
.... Midian
3. Hagar
.... Ishmael

Abraham has three wives: Sarah, Keturah, and Hagar. Issac is child of Abraham and Sarah. Jokshan and Midian are child(s) of Abraham and Keturah. Ishmael is child of Abraham Hagar.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

class Person
{
    protected:
        string name;
        int ID;
        string gender;
    public:
        Person(string name, int ID, string gender)
        {
            this-> name = name;
            this-> ID = ID;
            this-> gender = gender;
        }

        const string getName () const
        {
            return name;
        }

        const int getID () const
        {
            return ID;
        }

        const string getGender () const
        {
            return gender;
        }

        vector <string> children;

};

class Male: public Person
{
    private:
        vector <string> wife;
};


class Female: public Person
{
    private:
        vector <string> husband;
};

void displayAll (Person *arr[])
{
    int SIZE = 10;

    cout  << "\nThe people in the system are...\n";
    for(int i = 0; i < SIZE; i++)
    {
        cout << arr[i] -> getName () << " " << arr [i] -> getID () <<  " " << arr [i] -> getGender () << "\n";
    }
    cout << "\n";
}

void Male:: addWife (Person *arr [], string p1, string p2)
{
    int SIZE = 10;

    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p1)
        {
             arr [i] -> wife. push_back (p2);
        }
    }
}

void Female:: addChild (Person *arr[], string p1, string p2, string p3)
{
    int SIZE  = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p2)
        {
             arr [i] -> children. push_back (p3);
        }
    }
}

int main()
{

    const int SIZE = 10;
    Person *arr[SIZE];

    arr [0] = new Person ("Abraham", 1, "M");
    arr [1] = new Person ("Sarah", 2, "F");
    arr [2] = new Person ("Keturah", 3, "F");
    arr [3] = new Person ("Hager", 4, "F");
    arr [4] = new Person ("Issac", 5, "M") ;
    arr [5] = new Person ("Jokshan", 6, "M");
    arr [6] = new Person ("Midian", 7, "M");
    arr [7] = new Person ("Ishmael", 8, "M");
    arr [8] = new Person ("Rebekah", 9, "F");
    arr [9] = new Person ("Esau", 10, "M");

    displayAll(arr);

    addWife(arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham
    addWife (arr, "Abraham", "Keturah");
    addWife (arr, " Abraham ","Hagar");

    addChild(arr, "Abraham", "Sarah", "Isaac"); /// Issac is the child of 
    Abraham and Sarah
    addChild(arr, "Abraham", "Keturah", "Jokshan");
    addChild(arr, "Abraham", "Keturah", "Midian");
    addChild(arr, "Abraham", " Hagar", " Ishmael");


    displayChildren(arr, "Abraham"); /// Displays all children of Abraham and groups each children with a wife

    return 0;
}


This is what I have come up with as of now. However, there are three errors. The first error is an out-of-line definition of 'addWife' does not match any declaration in 'Male'. Second error is no member named 'wife' in 'Person'. The third error is an out-of-line definition of 'addChild' does not match any declaration in 'Female'. How would I fix these errors?
Last edited on
1
2
3
4
5
6
7
8
class Male: public Person
{
    public:
        void addWife (Person *arr [], string p1, string p2);

    private:
        vector <string> wife;
};



1
2
3
4
5
6
7
8
9
10
11
12
13
void Male:: addWife (Person *arr [], string p1, string p2)
{
    int SIZE = 10;

    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p1)
        {
             arr [i] -> wife. push_back (p2);
             wife. push_back (p2);
        }
    }
}


1
2
3
4
5
6
7
8
class Female: public Person
{
    public:
        void addChild (Person *arr[], string p1, string p2, string p3);

    private:
        vector <string> husband;
};
Thank you for the help. I tried implementing the following code into the program, but it is unable to compile. The errors are the "use of undeclared identifier 'addWife' " and the "use of undeclared identifier 'addChild' for the following lines.

1
2
addWife (arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham
addChild (arr, "Abraham", "Sarah", "Isaac"); /// Issac is the child of Abraham and Sarah 


Tried incorporating something like "arr [0] -> addWife" but to no avail.
Last edited on
Sorry, I know you know that the method must be called on an object - I had missed the line "Tried incorporating ...".
However:
As you already know, the methods addWife and addChild are both non-static members of a class and must be called on an object of the class, and the addWife method must be an object of type Male. The pointer to the class Person is called an "incomplete" pointer and can be used to call methods and reference data in the base class Person, but not the method addWife as it's not a polymorphic method (it's particular to the Male class).
No big deal - just explaining that you need to use Male, rather than Person in your new operator call. It's a simple mistake in your code.


arr [0] = new Person ("Abraham", 1, "M");

arr [0] = new Male ("Abraham", 1, "M");

Also, in order to call addChild, the object must be of type Female. So when you create a[1] for Sarah, it should be new Female("Sarah", 2, "F");
Then below you should probably use a[1] for Sarah since she's female.

Then, the below code, which you've already tried, should work fine.

1
2
3
4
5
6
7
8

addWife (arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham

arr [0]->addWife (arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham

addChild (arr, "Abraham", "Sarah", "Isaac"); /// Issac is the child of Abraham and Sarah

arr [1]->addChild (arr, "Abraham", "Sarah", "Isaac"); /// Issac is the child of Abraham and Sarah 


Also, note that you do not use the string p1 in the method addChild. The wife is connected to the husband, and the child to the wife. So you could recode the addChild method with one less (unused) parameter.
Last edited on
Thanks you so much for the help CPPAWhile. Would it be necessary to replace the Person constructor with two separate constructors for the initialization of type Male and Female? Should 'Person *arr[SIZE]' also be changed into 'Male *arr[SIZE]' and 'Female *arr [SIZE]' ?

EDIT: I am getting an error message "no matching constructor for initialization of 'Male'" and
and "no matching constructor for initialization of 'Female'" for the following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

class Person
{
    protected:
        string name;
        int ID;
        string gender;
    public:
        Person (string name, int ID, string gender)
        {
            this-> name = name;
            this-> ID = ID;
            this-> gender = gender;
        }

        const string getName ()
        {
            return name;
        }

        const int getID ()
        {
            return ID;
        }

        const string getGender ()
        {
            return gender;
        }

        vector <string> children;

};

class Male: public Person
{
    public:
        void addWife (Person *arr [], string p1, string p2);
    private:
        vector <string> wife;
};


class Female: public Person
{
    public:
        void addChild (Person *arr [], string p1, string p2, string p3);
    private:
        vector <string> husband;
};

void displayAll (Person *arr[])
{
    int SIZE = 10;

    cout  << "\nThe people in the system are...\n";
    for(int i = 0; i < SIZE; i++)
    {
        cout << arr[i] -> getName () << " " << arr [i] -> getID () <<  " " << arr [i] -> getGender () << "\n";
    }
    cout << "\n";
}

void Male:: addWife (Person *arr [], string p1, string p2)
{
    int SIZE = 10;

    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p1)
        {
             wife. push_back (p2);
        }
    }
}

void Female:: addChild (Person *arr[], string p1, string p2, string p3)
{
    int SIZE  = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p2)
        {
            children. push_back (p3);
        }
    }
}

int main()
{

    const int SIZE = 10;
    Person *arr[SIZE];

    arr [0] = new Male ("Abraham", 1, "M");
    arr [1] = new Female ("Sarah", 2, "F");
    arr [2] = new Female ("Keturah", 3, "F");
    arr [3] = new Female ("Hager", 4, "F");
    arr [4] = new Male ("Issac", 5, "M") ;
    arr [5] = new Male ("Jokshan", 6, "M");
    arr [6] = new Male ("Midian", 7, "M");
    arr [7] = new Male ("Ishmael", 8, "M");
    arr [8] = new Female ("Rebekah", 9, "F");
    arr [9] = new Male ("Esau", 10, "M");

    displayAll(arr);

    arr [0] -> addWife(arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham
    arr [0] -> addWife (arr, "Abraham", "Keturah");
    arr [0] -> addWife (arr, " Abraham ","Hagar");

    arr [1] -> addChild(arr, "Abraham", "Sarah", "Isaac"); /// Issac is the child of Abraham and Sarah
    arr [2] -> addChild(arr, "Abraham", "Keturah", "Jokshan");
    arr [2] -> addChild(arr, "Abraham", "Keturah", "Midian");
    arr [3] -> addChild(arr, "Abraham", " Hagar", " Ishmael");


    // displayChildren(arr, "Abraham"); /// Displays all children of Abraham and groups each children with a wife

    return 0;
}
Last edited on
To answer your questions:
"Would it be necessary to replace the Person constructor with two separate constructors for the initialization of type Male and Female? Should 'Person *arr[SIZE]' also be changed into 'Male *arr[SIZE]' and 'Female *arr [SIZE]' ?"

Yes, you need constructors implemented
No, you don't need to re-declare the Person *arr as Male * and/or Female *. You want a generic base class Person in your list. That's good design, nothing wrong there.

In the below code, I put in some quick fixes to get your code to compile. I need to take a look at one more issue - that of destruction. We have not emptied out the vectors via a declared and defined constructor so we are relying on the compiler-generated default for now, but at least we have a start, we can compile.
So the quick fixes needed:
- Just implement a constructor in each of the Male and Female classes.

Note 1: You cannot inherit a constructor. That is, you cannot create a instance of a subclass using a constructor of one of it's superclasses. ... Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

Note 2:
A default constructor is a constructor that either has no parameters, or if it has parameters, all the parameters have default values. If no user-defined constructor exists for a class A and one is needed, the compiler implicitly declares a default parameterless constructor A::A() .

About note 2: Once we DO define a constructor, the compiler will no longer implement a default, so we cannot now say Male *m = new Male; any more, unless we define a parameter-less constructor.

- Use the proper (sub-class) pointer to call the methods addWife or addChild.
- Hang the console up after your display method.
- Clean up the heap at the end of the program.
- There is one other very minor issue of cast types. There are special operators in C++ that can check types and possibly return NULL. Since
this is a simple program, we can leave that out for now.

When I get back to this, I'll look at the destructor issue and a slight issue with initialization of the vector objects - I didn't put anything in to initialize them in the new constructors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

class Person
{
    protected:
        string name;
        int ID;
        string gender;
    public:
        Person (string name, int ID, string gender)
        {
            this-> name = name;
            this-> ID = ID;
            this-> gender = gender;
        }

        const string getName ()
        {
            return name;
        }

        const int getID ()
        {
            return ID;
        }

        const string getGender ()
        {
            return gender;
        }

        vector <string> children;

};

class Male: public Person
{
	public:
    Male (string name, int ID, string gender) : Person(name, ID, gender)
        {  
        }
        void addWife (Person *arr [], string p1, string p2);
    private:
        vector <string> wife;
};


class Female: public Person
{
    public:
    Female (string name, int ID, string gender) : Person(name, ID, gender)
        {  
        }
        void addChild (Person *arr [], string p1, string p2, string p3);
    private:
        vector <string> husband;
};

void displayAll (Person *arr[])
{
    int SIZE = 10;

    cout  << "\nThe people in the system are...\n";
    for(int i = 0; i < SIZE; i++)
    {
        cout << arr[i] -> getName () << " " << arr [i] -> getID () <<  " " << arr [i] -> getGender () << "\n";
    }
    cout << "\n";
}

void Male:: addWife (Person *arr [], string p1, string p2)
{
    int SIZE = 10;

    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p1)
        {
             wife. push_back (p2);
        }
    }
}

void Female:: addChild (Person *arr[], string p1, string p2, string p3)
{
    int SIZE  = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p2)
        {
            children. push_back (p3);
        }
    }
}

int main()
{

    const int SIZE = 10;
    Person *arr[SIZE];

    arr [0] = new Male ("Abraham", 1, "M");
    arr [1] = new Female ("Sarah", 2, "F");
    arr [2] = new Female ("Keturah", 3, "F");
    arr [3] = new Female ("Hager", 4, "F");
    arr [4] = new Male ("Issac", 5, "M") ;
    arr [5] = new Male ("Jokshan", 6, "M");
    arr [6] = new Male ("Midian", 7, "M");
    arr [7] = new Male ("Ishmael", 8, "M");
    arr [8] = new Female ("Rebekah", 9, "F");
    arr [9] = new Male ("Esau", 10, "M");

    displayAll(arr);

	Male *m = (Male *) arr[0];
    m -> addWife(arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham
    m -> addWife (arr, "Abraham", "Keturah");
    m -> addWife (arr, " Abraham ","Hagar");

	Female *f1 = (Female *) arr[1];
    f1 -> addChild(arr, "Abraham", "Sarah", "Isaac"); /// Issac is the child of Abraham and Sarah
    Female *f2 = (Female *) arr[2];
    f2 -> addChild(arr, "Abraham", "Keturah", "Jokshan");
    f2 -> addChild(arr, "Abraham", "Keturah", "Midian");
    Female *f3 = (Female *) arr[3];
    f3 -> addChild(arr, "Abraham", " Hagar", " Ishmael");


    // displayChildren(arr, "Abraham"); /// Displays all children of Abraham and groups each children with a wife
    // After you finish your displayChildren method, you can use the below 2 lines of code to hang up the console 
    // to allow us to look at the family tree before exiting.
	cout << "Press enter (or Ctrl-C) to quit ... " << endl;
	cin.get();
	
	// Clean up the heap to avoid memory leaks in your program and memory starvation
	// in the operating system due to your program not freeing the memory it allocated.
	// Just call delete on each array member.
	for (int i = 0; i < 10; i++)
		delete arr[i];
    return 0;
}
Last edited on
Once again, thank you so much for the help and the wonderful explanations CPPAWhile.

I am currently working on the displayChildren method, but I may be approaching this wrong.

The initial version was the following ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Male: public Person
{
    public:
        Male (string name, int ID, string gender) : Person(name, ID, gender)
        {
        }
        void addWife (Person *arr [], string p1, string p2);

        void displayWifeChildren (Person *arr [])
        {
            for (int i = 0; i < wife.size (); i++)
            {
                cout <<  wife [i] -> getName () << "\n";

                for (int j = 0; j < children. size (); j++)
                {
                    cout << children [j] -> getName () << "\n";
                }
            }
        }
    private:
        vector <string> wife;
};


The current version is the following ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Male: public Person
{
    public:
        Male (string name, int ID, string gender) : Person(name, ID, gender)
        {
        }
        void addWife (Person *arr [], string p1, string p2);

        void displayWifeChildren (Person *arr [])
        {
            int i = 0;
            int j = 0;
            while (i < wife.size () )
            {
                cout << wife [i] -> getName () << "\n";

                while (j < children.size () )
                {
                    cout << children [j] -> getName () << "\n";
                }
            }
        }
    private:
        vector <string> wife;
};

void displayChildren (Person *arr[], string p1)
{
    int SIZE = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if (arr [i] -> getName () == p1)
        {
            cout << "Children of " << arr [i] -> getName () << " are ";
            displayWifeChildren (arr);

        }
    }
    cout << "\n";
}


There are five errors in total. The first and second error is a "member reference type 'value_type' is not a pointer". The third and fourth error is a "no member named 'getName' ". The fifth error is "use of undeclared identifier 'displayWifeChildren' ". Would something such as arr [wife [i]] -> getName () be a possible solution? Would it also be appropriate to put the displayChildren function in the derived Female class and call the function in the derived Male Class to display all of the children specific to a wife?
The first and second error was just that you had used "->" on an object. You had need to use "." which is how to reference a member on an object, while "->" is how to reference a member on a pointer. Then once that bug was fixed, the compiler complained (third and fourth errors) that there was no GetName() method because the vector class does not have that method. To get the "value" stored at some index of the vector, you can just use array notation as I show below.

The fifth error occurs because the method displayWifeChildren is a member of the Male class and you called it as if it was just a method (not in a class).

You might want to try ((Male *) arr[i]) -> displayWifeChildren (arr) instead.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class Male: public Person
{
    public:
        Male (string name, int ID, string gender) : Person(name, ID, gender)
        {
        }
        void addWife (Person *arr [], string p1, string p2);

        void displayWifeChildren (Person *arr [])
        {
            for (int i = 0; i < wife.size (); i++)
            {
                cout <<  wife [i] -> getName () << "\n";
             
                for (int j = 0; j < children. size (); j++)
                {
                    cout << children [j] -> getName () << "\n";
                }
            }
        }
    private:
        vector <string> wife;
};


The current version is the following ...

class Male: public Person
{
    public:
        Male (string name, int ID, string gender) : Person(name, ID, gender)
        {
        }
        void addWife (Person *arr [], string p1, string p2);

        void displayWifeChildren (Person *arr [])
        {
            int i = 0;
            int j = 0;
            while (i < wife.size () )
            {
                
                cout <<  wife [i] -> getName () << "\n";
                
                
                cout << wife [i] . getName () << "\n";
                
                cout << wife [i] << "\n";

                while (j < children.size () )
                {
                     
                    cout << children [j] -> getName () << "\n";
                    
                    
                    cout << children [j] . getName () << "\n";
                    
                    cout << children [j] << "\n";
                }
            }
        }
    private:
        vector <string> wife;
};

void displayChildren (Person *arr[], string p1)
{
    int SIZE = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if (arr [i] -> getName () == p1)
        {
            cout << "Children of " << arr [i] -> getName () << " are ";
            
           
           displayWifeChildren (arr);
           
           ((Male *) arr [i]) -> displayWifeChildren (arr);           
        }
    }
    cout << "\n";
}


To answer the last question:
Yes, it would be nice to put a displayChildren method in the Female class, and have another method displayWifeChildren in the Male class. This would make the method in the Male class almost trivial, while most of the current logic you have in Male can be moved into the displayChildren method of the Female class.

Another solution which would make your code a little safer as far as type-checking (albeit at the slight expense of virtual functions), is to declare a virtual method (it could be pure virtual or not, whatever suits you) in the Person class:

virtual void displayChildren() {}
or
virtual void displayChildren() = 0;


Then in both the classes Male AND Female, implement a method of the same name:

Male {
void displayChildren() {}

Female {
void displayChildren() {}

The Male's method could then just call the Female's method, and there is one more very big advantage: We would no longer need to cast the Person * arr[i] to a Male *. The overloaded methods would automatically be called, even if the type of the pointer is Person, because the actual object pointed to at runtime is a Male (as the compiler has filled in the v-table with a Male object and it's methods).

There are other solutions involving static and dynamic casts, but these only really work if the classes are already polymorphic which yours are not (although, again, they WOULD be if you used virtual functions as I discuss above).

It would be nice to have a little more certainty that the class is really a Male - we are using the fact that we know of a Male's name here, which is good enough for a small program. For a larger program, we would want greater certainty that we don't try to call a method on a Person or Female, when we wanted a Male. But using virtual functions (polymorphism) will at least lead to a graceful execution of the program.

Another comment: It might be better to have a Male class declare as a member, a list of the actual Person *'s for the wives:
eg. declare vector <Person *> wives;

And make sure each Person knows it's name as a string.
Your code will be more efficient if you do this as you could then iterate
over the wives and displayChildren for each wife. And each wife could in turn have a member vector <Person *> children;

I think just using the string vectors is a little cumbersome, as it lacks information. It's not wrong, it's efficient, storage-wise to do it your way.

I realize this means some re-writing of the program but if you don't do this, you will always be iterating over a long Person loop, and trying to find the Person *. You can simply have those Person *'s right in the object of the Male, ready to go right away. I guess it's simpler to have a list of Person's but perhaps it takes a bit more memory?

It's not a BAD idea to store "tree-like" information in an array since traversals of the trees can be put into an array - traversals such as inorder, postorder, and preorder.

Lastly, be careful of your strings for Abraham, Isaac (not Issac), Hagar (not Hager), etc.. This will cause errors if they are not exactly the same throughout the program.
Last edited on
I cannot thank you enough for the massive guidance and the time you've taken to help me, CPPAWhile. I'll keep your wonderful insights in mind for future iterations of this program. Definitely will look to try to improve it with virtual functions and to implement the vector <Person *> wives in the near future. But most certainty, I will have to go back and review the key concepts of introductory and object oriented programming as I feel my basic understanding is still lacking.

The program now complies without any errors. However, the displayWifeChildren function isn't displaying any of the names properly. It is only printing the first wife which is Sarah, and none of the children. Does anyone know what would be the appropriate step to fix this logic issue?
Last edited on
Your welcome.

A couple things to check in your program now that it compiles.

- Check your j index - it needs to be initialized back to 0 for each value of i.
- Double-check the spellings of names - fix any errors in the strings. Look for things like " Abraham" or "Issac" or "Abraham ". The strings must match exactly.
- The biggest problem once you fix those bugs is that the Person object you might be calling it on is a Male? A Male has 0 children. So make sure that you somehow call these methods on a Female. Remember that only a female Person object will have the children's name vector.
- Your matching on strings approach will work just try to look at each step of your program and get your for and while loops fixed up. You just have minor logical errors and minor bugs. It's tricky to have a lookup of a string and match with an object - it's not a trivial thing.
Last edited on
If you declare the below (albeit inelegant) method in the class Male, it will display that particular male's wives' children. You could then take the part of the loop with the k index and move it over to the Female as a displayChildren method. I simply fixed up your original method without structuring it in any special way. But at least you can get the children of a male out. I added Person *arr[] as a parameter to make things easier. Perhaps you can improve on this by splitting it up into two methods. At least it will give the following output:

3
Sarah
Isaac
Keturah
Jokshan
Midian
Hagar
Ishmael

For Abraham, you would call it as follows:
Male *m = (Male *) arr[0];
m->displayWifeChildren(arr);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void displayWifeChildren (Person *arr[])
        {
	    int SIZE = 10;
            int i = 0;
            int j = 0;
            cout << wife.size() << "\n";
            while (i < wife.size () )
            {
                cout << wife [i] << "\n";
				j = 0;
				for(int k = 0; k < SIZE; k++)
				{
					if(arr [k] -> getName() == wife[i])
					{
						while (j < arr [k] -> children.size () )
						{
							cout << arr [k] -> children [j] << "\n";
							j++;
						}
					}
				}
              i++; 
            }
        }


It would also be nice to improve the efficiency of the loop over k so we break out of it once we print out the children. The break statement can be use for that purpose or simply use the match as the test condition for the for loop. Sorry, I just hadn't thought of that until now or I would have showed you how.
Last edited on
Now that we have the children, you can make 1 of 2 enhancements.

Simply declare a method in the Person class as follows (note that I drop the displayWifeChildren and just say displayChildren):

virtual void displayChildren(Person *arr []) = 0;

Then rename the method displayWifeChildren to just displayChildren (in Male and in Female that is). You can implement an empty one in Female until you have time to recode it by using the Female to help out:
Female
{
void displayChildren() {}

Now we can call the method not only by using the above m pointer I initialized but also with just:

arr[0]->displayChildren(arr).

You will now have the same output as above:

3
Sarah
Isaac
Keturah
Jokshan
Midian
Hagar
Ishmael


Perhaps you could also push the method displayChildren(Person *arr[]) up into the base class Person since the list of children exists in that. Then if you made the method virtual you would have to be careful to call the base class's method from your Male and Female objects' implementations, and simply have any additional handling you might want in the Male and Female implementations of displayChildren(), or just don't make the method virtual in the first place, and skip the polymorphism.
Last edited on
Thanks again CPPAWhile. I've modified my code according to your suggestions. However, the program is outputting the following ...

Children of Abraham
1 Sarah
Issac
2 Keturah
Jokshan
Midian

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
 
#include <stdlib.h>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

class Person
{
    protected:
        string name;
        int ID;
        string gender;
    public:
        Person (string name, int ID, string gender)
        {
            this-> name = name;
            this-> ID = ID;
            this-> gender = gender;
        }

        const string getName ()
        {
            return name;
        }

        const int getID ()
        {
            return ID;
        }

        const string getGender ()
        {
            return gender;
        }

        vector <string> children;

};
class Male: public Person
{
    public:
        Male (string name, int ID, string gender) : Person(name, ID, gender)
        {
        }
        void addWife (Person *arr [], string p1, string p2);

        void displayWifeChildren (Person *arr[], string p1)
        {
            int SIZE = 10;
            int i = 0;
            int j = 0;

            cout << "Children of " << p1 << "\n";

            while (i < wife.size () )
            {
                cout << i + 1 << " " << wife [i] << "\n";

				j = 0;
				for(int k = 0; k < SIZE; k++)
				{
					if(arr [k] -> getName() == wife[i])
					{
						while (j < arr [k] -> children.size () )
						{
							cout << "\t" << arr [k] -> children [j] << "\n";
							j++;
						}
					}
				}

              i++;
            }
        }


    private:
        vector <string> wife;
};


class Female: public Person
{
    public:
        Female (string name, int ID, string gender) : Person(name, ID, gender)
        {
        }
        void addChild (Person *arr [], string p1, string p2, string p3);
    private:
        vector <string> husband;
};

void displayAll (Person *arr[])
{
    int SIZE = 10;

    cout  << "\nThe people in the system are...\n";
    for(int i = 0; i < SIZE; i++)
    {
        cout << arr[i] -> getName () << " " << arr [i] -> getID () <<  " " << arr [i] -> getGender () << "\n";
    }
    cout << "\n";
}

void Male:: addWife (Person *arr [], string p1, string p2)
{
    int SIZE = 10;

    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p1)
        {
             wife. push_back (p2);
        }
    }
}

void Female:: addChild (Person *arr[], string p1, string p2, string p3)
{
    int SIZE  = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if(arr[i]-> getName () == p2)
        {
            children. push_back (p3);
        }
    }
}

/*
void displayChildren (Person *arr[], string p1)
{
    int SIZE = 10;
    for (int i = 0; i < SIZE; i++)
    {
        if (arr [i] -> getName () == p1)
        {
            cout << "Children of " << arr [i] -> getName () << " are ";
            ((Male *) arr [i]) -> displayWifeChildren (arr);
        }
    }
    cout << "\n";
}
*/

int main()
{

    const int SIZE = 10;
    Person *arr[SIZE];

    arr [0] = new Male ("Abraham", 1, "M");
    arr [1] = new Female ("Sarah", 2, "F");
    arr [2] = new Female ("Keturah", 3, "F");
    arr [3] = new Female ("Hagar", 4, "F");
    arr [4] = new Male ("Issac", 5, "M") ;
    arr [5] = new Male ("Jokshan", 6, "M");
    arr [6] = new Male ("Midian", 7, "M");
    arr [7] = new Male ("Ishmael", 8, "M");
    arr [8] = new Female ("Rebekah", 9, "F");
    arr [9] = new Male ("Esau", 10, "M");

    displayAll(arr);

    Male *m = (Male *) arr [0];
    m -> addWife(arr, "Abraham", "Sarah"); /// Sarah is the wife of Abraham
    m -> addWife (arr, "Abraham", "Keturah");
    m -> addWife (arr, " Abraham ","Hagar");

    Female *f1 =  (Female *) arr [1];
    f1 -> addChild(arr, "Abraham", "Sarah", "Issac"); /// Issac is the child of Abraham and Sarah

    Female *f2 = (Female *) arr [2];
    f2 -> addChild(arr, "Abraham", "Keturah", "Jokshan");
    f2 -> addChild(arr, "Abraham", "Keturah", "Midian");

    Female *f3 = (Female *) arr [3];
    f3 -> addChild(arr, "Abraham", " Hagar", " Ishmael");

    m-> displayWifeChildren(arr, "Abraham");

    /*
    displayChildren(arr, "Abraham"); /// Displays all children of Abraham and groups each children with a wife
    */
    cout << "\nPress enter (or Ctrl-C) to quit ... " << endl;
	cin.get();

	for (int i = 0; i < 10; i++)
    {
        if ( arr [i] != NULL)
        {
             delete arr[i];
        }
    }

    return 0;
}



You copied the code properly and made some perfectly ok enhancements such as adding the Male and a few other formatting issues. You successfully outputted Abraham's first two wives' children, you were just missing Hagar's child.

The problems are simply the original bugs of the wrong strings as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Change "Issac" to Isaac

    arr [4] = new Male ("Issac", 5, "M") ;

    arr [4] = new Male ("Isaac", 5, "M") ;

// Change " Abraham " to "Abraham"

    m -> addWife (arr, " Abraham ","Hagar");

    m -> addWife (arr, "Abraham","Hagar");

// Change Issac to Isaac

   f1 -> addChild(arr, "Abraham", "Sarah", "Issac"); /// Issac is the child of 
   Abraham and Sarah

   f1 -> addChild(arr, "Abraham", "Sarah", "Isaac"); /// Isaac is the child of 
   Abraham and Sarah

// Change " Hagar", " Ishmael" to "Hagar", "Ishmael" 

   f3 -> addChild(arr, "Abraham", " Hagar", " Ishmael");

   f3 -> addChild(arr, "Abraham", "Hagar", "Ishmael");




You can keep the name Isaac as Issac if you want, it's only Hagar's child Ishmael that wasn't printing out.
Last edited on
Thanks for the help again. A lot of stuff came up in real life so I haven't been able to get back to this program.

Would it also be possible to incorporate a displayDescendants () function so that when called for Abraham, it would display all descendants with his children and wives first? Thus, the program would recursively call displayDescendants () for each of his children, and then display all the descendants.

I will begin working on the virtual functions implementation and possibly linked list.
I don't think it would be easy for the function displayWifeChildren (which I called displayChildren in my scratch work) to be used recursively due to the fact that you've used an "over-normalized" collection for the children (ie. you used a set of strings). Unfortunately, the vector that you used is a vector of strings (which made it easy to keep track of names and display them). The down-side is that you do not have an array of type class Person to pass if while in the method displayChildren or your method displayWifeChildren you were to call it again (and again). It would not work. There are many ways to solve the problem:

1) Have an array of Person * in each Person class, then you could simply keep calling things by passing the array. The function signature displayChildren(Person arr[]) would then work recursively. You could probably do this with vector<Person *> also.
2) Use integer indices into an array to keep track of the children, and simply keep a vector<int> of these in each Person class. Or as with 1) above, an array of them int *arr or int arr[].
3) Use while loops and lookups to do it non-recursively.
4) Use a hybrid approach involving your string-lists and some sort of lookup. This might be a "best-of-both-worlds" solution (I might try this tomorrow and give you a heads up on what I think).

Another comment, the
for(int k = 0; k < SIZE; k++)
{
should be improved on.

Keep in mind that each Person object has an id. You can simply keep an array of ids (the vector is nice because it's resizable so I would suggest that). And the vector template will hold onto things when you resize. And you just have to call clear() when you're through with it. vector<int> children would be resized or sized by children.resize(newsize, initializer), etc..

If you recode the system to use ids everywhere, it will be completely recursive. The only downside is that you still have to find the records in the master-list by doing a lookup, ie. for each array element, find the index of the array containing the id that is in the list of children. Then access the array for the information. It's not a bad way to do things.

The most powerful would be to have c or c++-style pointers but in this case, it's harder to build the tree up. Your way is fine (ie. using an array with ids). But if you use pointers, ie. a recursively defined class, recursion is almost trivial. Just remember you need base case in recursion. Then you can optimize by doing a non-recursive emulation of the recursion. This is often done to save on stack memory which can run out. So the recursion is the first step, followed by an unravelling approach where it's done non-recursively but it's almost like recursion.

I also have been busy for the last week, so I'll take a another look tomorrow.
Last edited on
Topic archived. No new replies allowed.