1
votes

I keep getting errors because Class A are private members. I want to avoid making them public. I thought making the Class B friend of A would fix this. I have created 2 class in 2 different header files as follows:

A.h

class A {
    friend class B;
private:
    std::string id;
    std::string name;
    std::string label;
public:
    A();
    ~A() {};
};

B.h

#include "A.h"

class B {
    friend class A;
private:
    std::string num;
    std::string data;
    std::vector<A> vec;
public:
    B();
    friend int display(B&);
    ~B() {};
};

B.cpp

int display(B& b){
// TESTING
    b.vec.resize(5);
    b.vec[0].id = "test";
    cout << b.vec[0].id << endl;
return 1;

}

error:

error: 'std::__cxx11::string A::id' is private within this context
2
What errors do you receive? - Roy Avidan
error: 'std::__cxx11::string A::id' is private within this context - CryptoDood
Maybe you can't pass friends in c++. Have you tried to create a private method in B which receive an A and return its id field? - Roy Avidan
I need to be able to write and read from A members with the vector created in class B. Because I am reading a file and putting the data in a A obj. - CryptoDood
An option is to make display a class function of B, not a friend function - Damien

2 Answers

0
votes
  • Using friends in C++ is usually (99% of the cases) an indicator that something is wrong with the design. Since if A wants to know the private data of B, and B wants access to the private data of A... that usually means that A and B want to get merried (i.e. be the same class) !

    • A usual solution in your case (with the few details supplied) is to make A a nested class of B.
    • Or perhaps you should consider which class is the 'face' of your structure: if you have in mind that your class' users should use only B, then A might be hidden as a PIMPL, (and then you'll expose an std::vector<std::shared_ptr<A>> as member of B, where A is an interface)

    • Think about it: you want A to have some members accessible only to B but to no one else... it doesn't work like that in OOP : if the class decides that something is public it should be public for all. Therefore - find ways to hide all of A begind B :)

0
votes

Problem

display is not a friend of A, so display cannot interact with id, a private member of A.

A is a friend of B. A can interact with the internals of B

B is a friend of A. B can interact with the internals of A

int display(B&) is not a member of B, but it is a friend of B. It can interact with the internals of B

int display(B&) is not a member of A or a friend of A. It can NOT interact with the internals of A. It does not inherit B's friendship with A through its friendship with B. Only A gets to decide who its friends are.

Solution

Decouple A and B by adding a display function to A and calling it from B's display function.

TL;DR Explanation

Easy fix is to make display a friend of A. Easy, but now you're punching more holes in the encapsulation of your classes. This is a bad solution.

It is generally preferable to use free functions, but the more the friend needs to know about the class's inner workings the more coupled they are and the less useful the recommendation becomes. display not only needs to know B has a vec, it must also know that the As in vec contain an id. To me that's pushing it too far. If anyone changes A or B, display likely needs to be changed. But what if you add

friend int display(A&);

to A and call this display overload from the B display overload? Now B's display only needs to know there is a display function for As and knows absolutely nothing about what the other display function does or what's in A.

display just knows it needs to call display for all the As in vec. It is a simple for loop.

Here what the code could look like:

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

class A {
    // no need to be a friend of B
    // const A& because a display function should not modify the displayed
    friend void display(const A&);
private:
    std::string id;
    std::string name;
    std::string label;
public:
    A(); // if this constructor doesn't do anything, remove it.
         // code that doesn't exist has no bugs.
    // destructor removed. See Rule of Zero
};

class B {
    // no longer any need to be a friend of A
private:
    std::string num;
    std::string data;
    std::vector<A> vec;
public:
    B(); // if this constructor doesn't do anything, remove it.
    friend int display(const B&);
    // destructor removed. See Rule of Zero
};

void display(const A& a){
// TESTING
    std::cout << a.id << std::endl;
}
int display(const B& b){
// TESTING
    for (const A & elem: b.vec) display(b.vec[0]);
    return 1; // does 1 really make sense here?
}

Note the absence of friendship except for the display functions.

The Rules of Three, Five, and Zero. Only Zero is relevant to this question, but familiarize yourself with the other rules. You cannot write complicated, efficient C++ code without knowing when and how to apply all three rules.

In general keep all of your functions as ignorant as possible and use helper functions to perform tasks they don't need to know the details of.