Search code examples
c++c++11c++14protected

How to allow objects of some classes to access protected members of an object of another class


I am looking for a solution to the following problem:

I have a class X with (assuming) only protected members. And I have a set S of several classes A, B, C, ...

Assuming there is an existing instance x of class X members (which instance may be, either an instance of class X, or a subset of an instance of any suitable container/derived-class/... depending on class X) :

  • Any instance s of a class of the set S, in relation with (depending on) x, must have access to protected members of x.
  • Access to protected members of x must be restricted to s (and x).
  • Creation and destruction of instance s must keep instance x alive and unaltered.

Additionally, at a given time, only one instance s [in relation] with x is existing.

In other words and to clarify the above requirements: I need that any instance s have access to protected members of class X, just like if (for example) classes of the set S were publicly derived from X, except that the subset of members coming from class X in inheritance must remain alive and unaltered whether instance s is created or destroyed.

In addition, the following requirements must be met:

  • X must be considered non-copyable and non-movable.
  • A solution involving wrapper to protected members of X, though acceptable, is not desirable due to maintenance cost.
  • Making all classes of set S friends of X is obviously not acceptable (to many classes).

The currently implemented solution, that though does not fulfills requirement #5, is using composition and a parent class for classes of S friend of X, e.g.:

class X
{
    // public: int get_prot();        // not allowed (rq#2)
    protected: int prot;
    friend class Xaxx;
    // friend A; friend B; ...        // not acceptable (rq#6)
};

class Xacc
{
protected:
    Xacc(X& x) : x(x) {}
    int& x_prot() { return x.prot; }  // not desirable (rq#5)
    X& x;
};

class A : public Xacc
{
public:
    A(X& x) : Xacc(x) {}
    void work()  { x_prot() = 1; }
};

Another interesting solution tested, that fulfills all requirements expect #4 though:

class A : public X
{
public:
    A(const X& x) : X(x) {}            // X not copyable (rq#4)
    void work()  { prot = 1; }
};

Any solution up to C++14 is acceptable. Thanks for your help.


Rationale:

To clarify where this problem comes from and in which way a solution will help me improve my code:

  • Every classes of the set S represent states of a state-machine (which state-machine is in some way inspired from the State pattern from the Gang of 4).
  • Each state must have access to a common underlying sub-object (the instance of X) which implements all sort of works (algorithms, i/o, and so on...)
  • When a new state is entered, an object of the proper class in set S is created; when the state is exited, the object is destroyed, then replaced by a new one (a new state). The instance of X must not be altered in this switch.

Solution

  • If your classes in S have a uniform interface that is simple (e.g. a single 'work' method), you can change your current implementation to fulfill requirement #5 by making Xacc a template class and by moving the implementation of the access to the protected parts of X into the specializations of Xacc. It would look like this:

    class X 
    {
    protected:
        int prot;
        template<typename State> friend class Xacc;
    };
    
    template<class State>
    class Xacc
    {
    public:
        Xacc(X &x) : x(x) {}
        void work();
    private:
        X &x;
    };
    
    class S1;
    template<> void Xacc<S1>::work()
    {
        x.prot = 1;
    };
    
    class S1: public Xacc<S1>
    {
    public:
        S1(X &x): Xacc<S1>(x) {}
    protected:
    };