2

Is there a way in C++ to enforce the use of getters or setters WITHIN the class?

class C{
   private:
   int x;        // should only be Changed by setX();

   private:
   setX(int i){
       (...)     // enforce some complicated invariantes
       x = i;
   };

   m(){
      x = 5;     // should not not allowed
   }
}

The only thing that comes to my mind is to put all the super-private members as private variables into an abstract base class with protected getters/setters.

But that doesn't sound like good practice.

Are there any more common ways or conventions to ensure, that everyone uses the setters?

(My purpose: If the class gets bigger i find it hard to remember what variables carry invariants with them. And currently before setting any variable I search, whether I created a getter or setter (to find out whether i have to consider invariants). But i want to get rid of this unprofessional searching-each-time.)

Hugo
  • 23
  • 5
  • 1
    Wouldn't you change the member variables to be their own classes then, maybe with templates to define the invariants? – Steve-o Aug 25 '11 at 08:35
  • The question is why would you actually care. Once you are the one modifying the class, anything that you can add to enforce that rule can be removed (and probably simplifying the code in the process)... Do you not trust yourself? Enforce a rule in your style guide, and just follow it, the compiler does not need to police it for you. – David Rodríguez - dribeas Aug 25 '11 at 08:41
  • @Steve-o: That would be as proposed by In silico below, wouldn't it?! – Hugo Aug 25 '11 at 08:48
  • @David Rodriguez - dribeas: Sorry, i don't get the idea: What do you mean by "anything enforcing the rule can be removed"? And what exactly could be a "rule in my style guide", that i could follow? I dont trust myself to remember for every variable to remember whether it was involved in some invariants. – Hugo Aug 25 '11 at 08:52
  • @Hugo: The point is that, if you're writing the code, you can _always_ get around the rules. If you're able to touch the insides of the class, then you can touch the insides of the class. If you need protection from yourself or those who would be modifying the class, then that's a much bigger problem. You also say, "If the class gets bigger": if you find your classes getting that big, then it seems clear that you should be splitting them up naturally. – Nicol Bolas Aug 25 '11 at 09:03
  • Just always use getters and setters for everything, so no need for your searching. – PlasmaHH Aug 25 '11 at 09:07
  • @Hugo I was thinking more of overloading the assignment operators, usage should be transparent to normal base types. – Steve-o Aug 25 '11 at 09:11
  • @Nicol Bolas: Thanks for the explanation. I just thought of some static-analysis help as you get it by typed languages to protect me from myself. – Hugo Aug 25 '11 at 09:37

4 Answers4

3

You can take advantage of composition like this:

class C_int
{
public:
    void setX(int i)
    {
        /* enforce invariants */
        x = i;
    }

private:
    int x;
};

class C
{
pulbic:
    void m()
    {
        x.setX(5);
        // x.x = 5; // Won't compile.
    }

private:
    C_int x;
};

But I find it odd that this is an actual problem for you, IMHO. You're the writer of the C class and its members. You have control over how the x variable is read/written, and x is not part of the public interface of the class. While it is true that other people (e.g. maintainers) can write code that breaks invariants in the future, unit tests should be able to cover those cases.

In silico
  • 51,091
  • 10
  • 150
  • 143
  • Ah, thanks, didn't thought of that possibility. But creating a wrapper still doesn't sound like a *nice* way... – Hugo Aug 25 '11 at 08:21
  • (Edited the original post and added my purpose) – Hugo Aug 25 '11 at 08:26
  • 2
    @Hugo, you shouldn't let your classes grow really big. It defeats the purpose of reusable modules, ie classes and OOP. – mike3996 Aug 25 '11 at 08:28
  • 2
    @Hugo: If a class gets so big that you start to forget important details of its responsibilities and how it works, then you should break them up into smaller classes. – In silico Aug 25 '11 at 08:29
  • Ok, thank you. Then i think i will have to increase my OOP skills instead of searching static guarantees for complex stuff... – Hugo Aug 25 '11 at 08:36
1

a) favor accessors throughout your codebase for consistency. then you will more easily spot direct accesses to members. if you need special accessors, then create special methods:

void setFoo(const t_foo& foo) {
    assert(foo.isValid());
    this->d_foo = foo;
}

void invalidateFoo() {
    this->d_foo = t_foo::InvalidFoo();
}

b) to get to an answer: i'll often create an inner class (temporarily in some cases):

class C {
    class t_inner {
    public:
    /* ... */
        void setX(int arg) {
            /* enforce some complicated invariants... */
            this->x = arg;
        }
        const int& getX() const {
            /* ... */
            return this->x;
        }
    private:
        int x;
    };
public:
/* ... */
private:
/* ... */
    void m() {
        this->inner().setX(5);
    }
private:
    t_inner d_inner;
};
justin
  • 104,054
  • 14
  • 179
  • 226
  • b) sounds OK to me. a) sounds as a lot of work, as most of the variables are not involved in any invariants. Or is this assumption wrong? – Hugo Aug 25 '11 at 08:54
  • 1
    re: 'a' yes, there is more boilerplate code to write, but it's consistent and very easy to maintain and debug since every mutation/access goes through a smaller set of points (compared to direct access). for example, you can ensure the variable `x` is always within a range when read using 1 line of code. for both of those reasons, it encourages you to approach programs in a more oo fashion - but some people just don't like to write that way and think it's a lot of work or overhead. in the end, it's your preference. i have written with enforced accessors for years and prefer it. – justin Aug 25 '11 at 09:17
0

The clean answer is No. There is no language feature for that; you can do some variation to achieve it, but that will clutter your code.

iammilind
  • 68,093
  • 33
  • 169
  • 336
0

In Visual C++, __declspec( property ) can be used to have this feature:

__declspec(property(put=setFunction, get=getFunction)) data-type property-name; 

See this article

Ajay
  • 18,086
  • 12
  • 59
  • 105