Accessing member in nested composition in C++

151 views Asked by At

If I have a class that contains another class (through composition) which in turn, contains another class. For example: a Teacher class containing a PersonalDetails class, that contains a ContactInformation class.

ContactInformation class:

class ContactInformation
{
private:
    std::string m_email{};
    std::string m_phoneNumber{};

public:
    ContactInformation(const std::string &email, const std::string &phone)
        : m_email{ email }, m_phoneNumber{ phone }
    {
    }

    // Solution 1
    const std::string& getEmail() const { return m_email; }
    const std::string& getPhoneNumber() const { return m_phoneNumber; }

    // Solution 2
    const ContactInformation& getContactInfo() const { return *this; }

    // Solution 3
    friend class Teacher;
};

PeronalDetails class:

class PersonalDetails
{
private:
    ContactInformation m_contact;
    std::string m_name;

public:
    PersonalDetails(const ContactInformation &info, const std::string &name)
        : m_contact{ info }, m_name{ name }
    {
    }


    // Solution 1
    const std::string& getEmail() const { return m_contact.getEmail(); }
    const std::string& getPhoneNumber() const { return m_contact.getPhoneNumber(); }
    const std::string& getName() const { return m_name; }


    // Solution 2
    const ContactInformation& getContactInfo() const { return m_contact.getContactInfo(); }
    const PersonalDetails& getPersonalDetails() const { return *this; }

    // Solution 3
    friend class Teacher;
};

Teacher class:

class Teacher
{
private:
    PersonalDetails m_details;
    double m_salary{};

public:
    Teacher(const PersonalDetails &details, double salary)
        : m_details{ details }, m_salary{ salary }
    {
    }

    // Solution 1
    const std::string& getEmail() const { return m_details.getEmail(); }
    const std::string& getPhoneNumber() const { return m_details.getPhoneNumber(); }
    const std::string& getName() const { return m_details.getName(); }
    double getSalary() const { return m_salary; }

    void printEmail1() const 
    {
        std::cout << getEmail() << '\n';
    }


    // Solution 2
    const ContactInformation& getContactInfo() const { return m_details.getContactInfo(); }
    const PersonalDetails& getPersonalDetails() const { return m_details.getPersonalDetails(); }

    void printEmail2() const 
    {
        std::cout << getContactInfo().getEmail() << '\n';
    }


    // Solution 3
    const std::string& getTeacherEmail(const ContactInformation &c) const
    {
        return c.getEmail();
    } 

    void printEmail3() const
    {
        std::cout << getTeacherEmail(getContactInformation());
    }

};

What is the "proper way" for the Teacher class to access the members (m_email & m_phoneNumber) in ContactInformation (the most "nested" class)? Neither of the solutions I can come up with seem all that great.

Solution 1; is to have getters methods for the member variables in all of the classes. But this seems like a bad idea since the Teacher class will end up with a lot of getters methods. Especially if I were to add more classes in the future.

Solution 2; is to return the instance itself. I don't know if this is better or if it breaks any best practices. But you can use the instance in the Teacher class to call getEmail() on it.

Solution 3; is using friend classes (don't have a lot of experience using them). but since you still have to pass an instance of ContactInformation in order to get m_email. It doesn't seem much different from Solution 2.

Is there any way of making the Teacher class a friend (or something) with the ContactInformation class so I can do something like this:

teacher.getEmail(); 

Without having to have any getters except from the one in ContactInformation?

1

There are 1 answers

0
Ivan On

The problem with friends classes is that you will lose the posibility (in a future) of using ContactInformation for a different class than Teacher without really gaining much from that.

If PeronalDetails is a member of Teacher and ContactInformation is a member of PeronalDetails. you could simply teacher.personalDetails.contactInformation.m_email which is quite long and requires all these members being public.

A midlle point can be a personalized getter:

public:
Teacher::getEmail(){
    return personalDetails.contactInformation.m_email;
}