3
class A;

class B {
private:
    int x = 3;
protected:
    int y = 4;
public:
    int k = 5;
};

I would like A to be able to access B's public and protected members, but not its private members.

I do not want A to derive from B.

-In my program, I have class A which manipulates class B. I have built a system using these two classes, and thus B should only be able to manipulate A's private variables through A's protected functions.

What is the most elegant way to do this? Is it good practice to lay expose the variables in such a way?

expl0it3r
  • 325
  • 1
  • 7
  • Why don't you want to derive `A` from `B` ? Thats exactly the function `protected` member access is meant for – Odysseus Aug 18 '20 at 16:43
  • 1
    @Odysseus but then every `A` instance would be a `B` instance. There can be a lot of reasons why that is not desirable. – Miguel Aug 18 '20 at 16:44
  • 2
    This sounds like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Surely, there's a better way to model the system you're trying to model without awkwardly coupling the two classes. C++ provides two ways to couple classes: inheritance (B really is an A and thus gets to see it all) and friendship (A trusts B completely). Even both of those should be used sparingly (tight coupling), and more complicated visibility constraints like this will likely only serve to confuse. – Silvio Mayolo Aug 18 '20 at 16:45
  • @Miguel Yes - but it still is the purpose of `protected`. So rearranging the classes seems reasonable for me – Odysseus Aug 18 '20 at 16:53
  • 1
    @Odysseus A mammal could derive from a respiratory system class; this does not mean that the mammal itself is a respiratory system – expl0it3r Aug 18 '20 at 16:54
  • @Odysseus Yes, the purpose of `protected` is to provide access to child classes, but that is not a reason to make a class inherit from another just to gain access. That is just bad design and the reason why `friend` exists. – Miguel Aug 18 '20 at 16:57

2 Answers2

1

The only solution I can think of is to take advantage of the non-associativity of friends relationships. If you contain the private members of B in another class and make B its friend, it would work.

Here's an example of what I say.

class B{
    private:
        class private_of_B{
        private:
            friend class B;
            int x = 3;
        };
        private_of_B private_members;
        friend class A;
    
    protected:
        int y = 4;
    
    public:
        int k = 5;
};
class A{
    public:
        void f(B& b) const{
            cout << b.y << endl;
            // good
            cout << b.k << endl;
            // good
            cout << b.private_members.x << endl;
            // error
        }
};

Anyways, this workaround seems like bad design... maybe the best thing you can do is redesign your class architecture.

Also, you might find this question useful.

Miguel
  • 2,130
  • 1
  • 11
  • 26
  • The base class super_B is a member of the class B. So the class A is able to access the member of an object of the class B. – Vlad from Moscow Aug 18 '20 at 17:07
  • `super_B` is not a member of `B`, it's its parent class, so the class `A` is not allowed to access any non-public member of it. – Miguel Aug 18 '20 at 17:11
  • Run this program. #include class A; class super_B{ protected: int x = 3; }; class B: public super_B{ protected: friend class A; int y = 4; public: int k = 5; }; class A { public: void f( const B &b ) const { std::cout << b.x << '\n'; } }; int main() { B b; A().f( b ); return 0; } – Vlad from Moscow Aug 18 '20 at 17:12
  • I changed the answer and tested it, so it works now. But as I say, I would recommend some redesign. – Miguel Aug 18 '20 at 17:30
0

If you don't want to change B, you can create a sort of a proxy class:

class B {
private:   
  int x = 3;
protected: 
  int y = 4;
public:    
  int k = 5;
};

class C : public B {
  friend class C_Proxy;
  // constructors...
};

class C_Proxy {
public:
  friend class A;
  C_Proxy(C& c) : y(c.y), k(c.k) { }
private:
  int& y;
  int& k;
}

(You can also arrange C_Proxy to handle proxying constant B's but that's not the focus of the question.)

einpoklum
  • 118,144
  • 57
  • 340
  • 684