C++ : How to ensure that a class member variable is modifiable only within a certain method

Viewed 2123

I am using C++ 14 with clang on MacOS Sierra. I want to enforce a rule by design. Following is the rule.

I have a member variable in my class say:

unsigned int m_important_num;

There are 4 methods in my class.

fun1();
fun2();
fun3();
fun4();

Objective:
I want only fun2() to be able to change the value of m_important_num.

Question:
Is it possible to make it compiler error if any method other than fun2() changes the variable? One possible way is to declare it const somehow empower fun2() to change const variables? Is this a good solution? Or are their any better solutions?

Secondary question:
Is it a wrong design to try do such a thing?

6 Answers

Sort of, with additional layer:

class S1 {
public:
    void fun2() { /*Modify m_important_num */ }
    unsigned int getImportantNum() const { return m_important_num;}
private:
    unsigned int m_important_num;
};
class S2 : private S1
{
public:
    void fun1();
    using S1::fun2; // or void fun2() {S1::fun2();}
    void fun3();
    void fun4();
};

As Yakk commented, if func2 need access to S2 members, CRTP can solve that:

template <typename Derived>
class S1 {
public:
    void fun2() { asDerived().foo3(); /*Modify m_important_num */ }
    unsigned int getImportantNum() const { return m_important_num;}
private:
    Derived& asDerived() { return stataic_cast<Derived&>(*this); }
private:
    unsigned int m_important_num;
};
class S2 : private S1<S2>
{
    // friend class S1<S2>; // If required.
public:
    void fun1();
    using S1::fun2; // or void fun2() {S1::fun2();}
    void fun3();
    void fun4();
};

Encapsulate it down. Put m_important_num in its own class. Aggregate it in your existing class. Have a getter for it. Then put fun2() as a member function of your inner class.

I little variant (if I understand correctly) of the Jeffrey solution: put the variable in an inner class and make it private; create a public getter and make func2() friend to the inner class.

I mean

struct foo
 {
   int f1 () { return b0.getVal(); }; // you can read `val` everywhere
   void f2 () { b0.val = 42; }; // you can write `val` in f2()
   void f3 () { /* b0.val = 42; ERROR ! */ }; // but only in f2()

   class bar
    { 
      private: 
         int val = 24;

      public:
         int getVal () { return val; }

      friend void foo::f2 ();
    };

   bar b0;
 };

In other words: friend is your friend.

If you want to prevent a method from modifying any member in the class you can use the trailing const identifier:

class something{
private:
  unsigned int var;

public:
  void fun1() const;
  void fun2();
  void fun3() const;
  void fun4() const;     
}

Here, only fun2() will be able to modify the variable.

I know there are lots of good answers, but there is also an option that you sort of alluded to in your question:

One possible way is to declare it const somehow empower fun2() to change const variables?

#include <iostream>

using uint = unsigned int;

class Test
{
    const uint num;

public:

    Test(uint _num)
        :
          num(_num)
    {}

    uint get_num() const
    {
        return num;
    }

    void can_change_num(uint _new_num)
    {
        uint& n(const_cast<uint&>(num));
        n = _new_num;
    }

    void cant_change_num(uint _new_num)
    {
//      num = _new_num; // Doesn't compile
    }

};

int main()
{
    Test t(1);

    std::cout << "Num is " << t.get_num() << "\n";

    t.can_change_num(10);

    std::cout << "Num is " << t.get_num() << "\n";

    return 0;
}

Produces

Num is 1
Num is 10

You already got lots of good answers to your primary question. I'll try to address the secondary one.

Is it a wrong design to try do such a thing?

It's hard to say w/o knowing more about your design. In general anything like this detected during a code review would raise a big red flag. Such a protection makes sense in a case of a big class with convoluted logic/implementation. Otherwise why would you like to go an extra mile and make your code much more complicated? The fact you seek for this can indicate your class became unmanageable.

I'd recommend to consider splitting it to smaller parts with better defined logic where you won't worry such mistakes can happen easily.

Related