-4

I was writing some exercise code to understand class inheritance and I couldn't figure out If there is a way to do what I tried to explain in the title.

So what I want to do to have Family and Member (family members) class. A family can have multiple members but members can have only one family. But let's say one of the members got married. So they are changing their family from A to other family B. Is there a way to do that?

#include <iostream>
#include <string>

class Family {
    std::string _familyName;
public:
    Family();
    void setFamilyName(std::string& str) { _familyName = str;}
    void printFamilyName() {
        std::cout << "Family name is: " << _familyName << std::endl;
    }
}

class Member : public Family {
    std::string _firstName;
public:
    Member();
    void setFirstName(std::string& str) { _firstName = str;}
    void changeFamily(Family *f) {} // What do i do here ?

}

int main(){
    Member m;
    m.setFamilyName("Smith");
    m.setFirstName("John");
    m.printFamilyName();
    Family f;
    B.setFamilyName("Williams");
    m.changeFamily(&f);
    m.printFamilyName();
    return 0;
}

From this, I would like to get an output as

Family name is: Smith
Family name is: Williams

Thank you.

(An extra question is, is there a way to create a member variable without constructing the family part, but to bind the new member varible to an existing family variable while declaring the member ?)

korel
  • 1
  • 1
  • 3
  • 2
    _"A family can have multiple members but members can have only one family."_ Sounds like a serious design flaw to me, since that doesn't relect reality. – πάντα ῥεῖ Feb 07 '19 at 22:02
  • 1
    You have a false inheritance - a member of a family is not a family. Trying to learn how to use inheritance by using exemplars like families is simply the wrong way to go. –  Feb 07 '19 at 22:02
  • @NeilButterworth I'm open to any suggestions for better learning. – korel Feb 07 '19 at 22:05
  • @korel My advice would be to avoid using inheritance unless you absolutely need it, which is usually when you want polymorphism. I don't see any polymorphism in your code, so you almost certainly don't need to use inheritance. Use composition. –  Feb 07 '19 at 22:07
  • 1
    Inheritance usually means is-a, in your case a Member is-a Family which is not right. A Member should be part of a Family which is composition. – Ian4264 Feb 07 '19 at 22:14
  • What you really have is a `Person` who has a name, and a `Family` (which contains their surname). You may represent this by a _pointer_ to that `Family`. `Family` may then contain a container of its people (or not). Using inheritance for this is wrong. – Tas Feb 07 '19 at 22:15
  • I see, thank you very much! I am not coming from a programming background so I wasn't aware of composition concept. I'll study that – korel Feb 07 '19 at 22:16
  • See what the Liskov principle is. You are definitely braking it. – Matthieu Brucher Feb 07 '19 at 22:17

3 Answers3

1

A family can have multiple members but members can have only one family. But let's say one of the members got married. So they are changing their family from A to other family B. Is there a way to do that?

You need the relationship defined between objects, not the object types. You can do that by using a container-contained relationship between objects, not a base class - derived class relationship between the classes.

struct Member;
struct Family
{
   std::vector<Member*> members;
}

struct Member
{
   Family* family;
}

I'll let you figure out how define the member functions of the classes so the relationships between the objects and the lifetimes of the objects are maintained in a robust manner.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

Short answer: No. Inheritance is set at compile time, you can't change it at runtime.

But I don't think inheritance is what you're looking for. Member inherits all the members and functions of Family. It doesn't imply a relationship between that Member and any particular instance of Family. This is reflected in your design - _familyName is a string, not a reference to an object or something. But you can have references to other objects. For example, like this:

#include <iostream>
#include <string>
#include <vector>

class Family {
    std::string _familyName;
public:
    Family(std::string name) : _familyName(name) {};
    void printFamilyName() {
        std::cout << "Family name is: " << _familyName << std::endl;
    }
}

class Member {
    std::string _firstName;
    std::vector<Family*> families;
public:
    Member(Family *familyPtr) { addFamily(familyPtr) };
    void setFirstName(std::string& str) { _firstName = str;}
    void addFamily(Family *familyPtr) { families.push_back(familyPtr) };
    void printFamilies() { for (auto &ptr : families) { ptr->printFamilyName() } };
}

int main()
{
    Family Jackson("Jackson");
    Family Williams("Williams");
    Member John(&Jackson);
    Member Alice(&Williams);
    John.printFamilies();
    John.addFamily(&Williams);
    John.printFamilies();
    return 0;
}

Member doesn't need any of the functions or values of class Family, so we don't inherit them. Instead we maintain a list of pointers to Family objects that this Member "belongs" to, thus implying a relationship between this Member and those instances of Family. When we want to print all the Family objects that Member owns, we just loop through the vector of pointers and call printFamilyName() on each of them.

Brandon Lyons
  • 473
  • 3
  • 10
  • 1
    Thank you very much. Now I have a more clear understanding of inheritance and I'll look up the other concepts that are mentioned in the comments. – korel Feb 07 '19 at 22:29
  • You should note this doesn't even compile: you need to pass a string to each `Family` (e.g. `Family Jackson("Jackson");` and a `Member` takes a `Family` as a pointer so you need to use `&` (e.g. `Member John(&Jackon);`) – Tas Feb 07 '19 at 23:16
  • Whoops. I'm still figuring out a good way to test my answers here without making a full blown Visual Studio project for each of them. Answer has been fixed. – Brandon Lyons Feb 07 '19 at 23:27
0

I don't think you need inheritance, and what you perceive to be inheritance is not correct. When working with multiple classes that work together either it be one class containing another or parent child relationship, there is always one question to ask yourself: The relationship between the two classes is it a "Is a relationship" or is it "Has a relationship". When you ask yourself that question and it is very easy to answer then you should know what design type your classes will need.

For example: A dog is a mammal and A dog has paws. So if we have 3 classes here. The structure would look something like this:

class Mammal {
    /* code */
};

class Dog : public Mammal {
    /* code */
};

Here we have inheritance because a Dog IS A Mammal and will inherit all of the traits that all Mammals have!

Next we would have this:

class Paws {
    /* code */
};

So let's go back to our "dog" class

class Dog : public Mammal {
private:
    Paws paws; // this is a member of Dog because a Dog HAS PAWS!
};

I hope this clears things up with the basic structure of classes!


Now to look back at your initial problem, here is what I have done. I made a basic struct to represent a FamilyMember where they all have the same information about each other in common. I abstracted out the last name completely to remove any dependencies and having to check if the last name is the same and to update or replace it etc. My code looks like this:

struct FamilyMember {
    std::string firstName_;
    unsigned int age_;
    char gender_;

    FamilyMember() = default;
    FamilyMember(const std::string& firstName, unsigned int age, char gender) :
        firstName_(firstName),
        age_(age),
        gender_(gender)
    {}
};

class Family {
private:
    std::string familyName_;
    std::vector<FamilyMember> members_;

public:
    Family() = default;
    explicit Family(const std::string& familyName) : familyName_( familyName ) {
    }

    void addMember(FamilyMember& member) {
        members_.push_back(member); 
    }

    FamilyMember& getMember(unsigned int idx) {
        return members_.at(idx);
    }

    std::vector<FamilyMember>& getFamily() {
        return members_;
    }

    const std::string& getFamilyName() const { return familyName_; }
};

int main() {
    Family Smith("Smith");
    FamilyMember John("John", 32, 'M');
    FamilyMember Sarah("Sarah", 29, 'F');
    FamilyMember Amanda("Amanda", 19, 'F');
    Smith.addMember(John);
    Smith.addMember(Sarah);
    Smith.addMember(Amanda);

    std::cout << "Meet the " << Smith.getFamilyName() << "s:\n";
    for (auto& m : Smith.getFamily()) {
        std::cout << m.firstName_ << " " << Smith.getFamilyName() << " " << m.age_ << " " << m.gender_ << '\n';
    }

    Family Jones("Jones");
    FamilyMember Tom("Tom", 44, 'M');
    FamilyMember Mary("Mary", 43, 'F');
    FamilyMember Mike("Mike", 21, 'M');
    Jones.addMember(Tom);
    Jones.addMember(Mary);
    Jones.addMember(Mike);

    std::cout << "Meet the " << Jones.getFamilyName() << "s:\n";
    for (auto& m : Jones.getFamily() ) {
        std::cout << m.firstName_ << " " << Jones.getFamilyName() << " " << m.age_ << " " << m.gender_ << '\n';
    }

    std::cout << "We present to you today the Union between: "
        << Jones.getMember(2).firstName_ << " " << Jones.getFamilyName() << " and "
        << Smith.getMember(2).firstName_ << " " << Smith.getFamilyName() << '\n';

    Jones.addMember(Amanda);
    std::cout << "We now have Mr. " << Jones.getMember(2).firstName_ << " " << Jones.getFamilyName() << " and "
        << "Mrs. " << Jones.getMember(3).firstName_ << " " << Smith.getFamilyName() << " " << Jones.getFamilyName() << '\n';    

    return 0;
}

So as you can see from the small program above; we do not have the dependency of having to modify the last name at all. With this kind of structure, when "Amanda" marries "Mike". She still belongs to the "Smith" family, but she has also joined with the "Jones" family. Now if you try to access her individual data through the structure, there is no information about her last name. You can retrieve her first name, age and gender. If you want the information for the last name, you have to retrieve that from the Family class itself as it never changes!

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59