简体   繁体   中英

How to restrict access to a non-const public member m of a class A to be const to a another class B, containing A as a non-const member?

Good evening!

I find this question to be interesting for other people, too. So I choose this one to finally ask my first question on stack overflow. A minimal description of the situation is the following:

class A {
public:
    Type_m_first m_first;
    Type_m_second m_second;
}

class B { // shoud be const on a.m_first
private:
    A& a; 
public:
    B(A& a_) : a(a_) {};
}

EDIT: Setting the members of A to be private and using getters and setters is not a solution to my problem. However, I hope for a solution as easy as this, because I well might miss something easy, due to my lack of experience in programming.

Now, I need any yet-to-be-implemented method of B to have only const access to A.m_first while having non- const access to A.m_second , and similarly, I need any other code using something of type B having the same access restrictions, when accessing Ba . Of course, this is impossible (at least from my humble point of view).

Nevertheless, my question is:

How is it possible to enforce on the class B such a const -restriction of the access to a non- const member variable?

EDIT With private m_first and m_second and a getters and setters for m_first and m_second the situation would be the same. Then the questions is: How to restrict the access of B to the const getter of m_first , and deny B the access to the setter of m_first , while at the same time allow B to use the setter of m_second ?


The above is the question. However, without the following context this question might be incomplete as it illustrates the significance of the question. The actual situation I am facing is the following:

class A {
public:
    Type_m_first m_first;
    Type_m_second m_second;
public:
    // A lot of code.
private:
    // A lot of code.
}

class B { // shoud be const on a.m_first
private:
    A& a;
public:
    B(A& a_) : a(a_) {};
private: 
    // A lot of code, that I am supposed to move from somewhere else to here
    // or write myself.
public: 
    // A lot of code, that I am supposed to move from somewhere else to here
    // or write myself.
}

class C { // shoud be const on a.m_second
private:
    A& a;
public:
    C(A& a_) : a(a_) {};
private: 
    // A lot of code, that I am supposed to move from somewhere else to here
    // or write myself.
public: 
    // A lot of code, that I am supposed to move from somewhere else to here
    // or write myself.
}

/* 
 * A lot of other Code that is supposed to work with A, B and C. The following three
 * functions serve as an example. As you see, everything is manipulating essentially 
 * the same data from a.
 */ 

 void f(A& a, /* other args */ ) { /* ... */ }; 
 void g(B& b, /* other args */ ) { /* ... */ }; // shoud be const on b.a.m_first by design
 int h(C& c, /* other args */ ) { /* ... */ };  // shoud be const on c.a.m_second by design
 
 int main() {
     A a; 
     B b = B(a);
     C c = C(a);

     f(a, /* other args */ );
     g(b, /* other args */ );
     return h(c, /* other args */ );
 }
  

Again, I need any yet-to-be-implemented method of B to have only const access to A.m_first while having non- const access to A.m_second . However, for the class C I need this to be exactly the other way round: I need any yet-to-be-implemented method of C to have only const access to A.m_second while having non- const access to A.m_first . Similarily, any Code using something of type B and C should have corresponding access-restrictions.

Again, of course, the question arises: Why do I need this? And the answer is, that the logical structure of the algorithm would be enforced by such a design. Making everything public is only in so far a concern, as neglecting the logical structure by accident leads to hard to find bugs in the code and due to the complexity of the code it is hard to keep track of such restrictions which are not enforced by design.

The best solution I had come up with - but did not yet implement -, is copying the code to two wrapper classes:

class A_first {
public:
    const Type_m_first& m_first;
    Type_m_second& m_second;
    A_first(A&) ; m_first(const A.m_first), m_second(A.m_second) {};
public:
    // Same code as before.
protected:
    // Same code as before.
private:
    // Same code as before.
}

class A_second {
public:
    Type_m_first& m_first;
    const Type_m_second& m_second;
    A_first(A&) ; m_first(A.m_first), m_second(const A.m_second) {};
public:
    // Same code as before.
protected:
    // Same code as before.
private:
    // Same code as before.
}

This is not desirably, because the code changes a lot over time, and keeping track of the changes in three classes is error-prone. My question is, what to do in such a situation?

I'm highly in doubt whether this is worth the effort, but this might suit your needs:

Instead of passing B and C a reference to A (which would grant them unlimited access to A , which is your whole problem if I understand correctly), only pass them the accessors to the two members. This could look like so:

class B {
private:
    std::function<Type_m_first const&()> getConstFirst;
    std::function<Type_m_second&()> getNonConstSecond;
public:
    B(std::function<Type_m_first const&()> f1, std::function<Type_m_second&()> f2)
        : getConstFirst(std::move(f1)), getNonConstSecond(std::move(f2)) {};

    void someMethod() {
        getConstFirst() = abc; // this won't compile
        getNonConstSecond() = xyz; // this will
    }
}

Analogous implementation for C .

Then, pass functions to the c'tors of B and C using lambdas that capture the instance of A by reference:

A a; 
B b = B(
    [&a]() -> auto const& { return a.m_first; },
    [&a]() -> auto& { return a.m_second; }
    );
C c = C(
    [&a]() -> auto& { return a.m_first; },
    [&a]() -> auto const& { return a.m_second; }
    );

Ok, I have to confess, I just skimmed reading you question, so I might have missed some details. But wouldn't a getter that returns a const reference be what you need?

class A {
private:
    Type_m_first m_first;
public:
    const Type_m_first & get_m_first() const { return m_first; }

    Type_m_second m_second;
}

Here only members of A might modify m_first directly. Everybody else must use the the const reference obtained by get_m_first .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM