2

What is the point of doing this:

class thing {
    
public:
    void setMarbles(int _marbles){marbles = _marbles;}
    void getMarbles(){return marbles;}
    
private:
    int marbles;
};

when you can just do this:

class thing {
    
public:
    int marbles;

};

I feel like this is a super common question, but I cant find an answer anywhere. My only theory is that if the order ov variables are changed in a new version of the class, programs that are using the old class will have the variables switched. Is this the correct reason?

sammonius
  • 53
  • 1
  • 5
  • The reason is encapsulation. It is a matter of opinion, but those who always use getters and setters do so to hide the implementation. It makes it easier to make changes in the future. For example if you want to add debugging code, want to log changes or add some sort of bounds checking to `marbles` you can easily do so if you used setters and getters initially. Otherwise, it becomes a lot harder to make these changes. – François Andrieux Jul 29 '22 at 13:52
  • 5
    getter and setters are a typical pattern in OOP programing. Personally I don't use them unless the I need to maintain some sort of invariant that relies on knowing when the state of a member is changed. – NathanOliver Jul 29 '22 at 13:52
  • Because people think it makes their code more “object oriented”. – Pete Becker Jul 29 '22 at 19:56
  • @PeteBecker I'm using a library called Qt, which is built by a billian-dollar company that uses C++ features I didn't even know existed, and they use it... – sammonius Jul 29 '22 at 20:28

3 Answers3

5

Encapsulation, maintainability, debugability

Consider:

void thing::setMarbles(int _marbles)
{
    // runtime error if incorrectly used
    assert(_marbles > 0);
    marbles = _marbles;
}

void thing::setMarbles(int _marbles)
{
    // log to some file to debug some specific scenario
    someLogUtility("Marbles write", _marbles);    
    marbles = _marbles;
}

You could also want to change thing in the future:

private:
char marbles; // support less marbles

or

private:
long marbles; // support more marbles

and then, you get to keep your setter without breaking external user code.

It is a really good practice, but it takes some mileage on real projects to see the full value. The short of it is: a public field ties your hands forever. A setter leaves flexibility.

Also, as suggested in comments:

void thing::setMarbles(int _marbles)
{
    // setting a breakpoint on next line is possible and lets you easily find where/when marbles is modified.
    marbles = _marbles;
}
Jeffrey
  • 11,063
  • 1
  • 21
  • 42
  • 1
    About debugability, you could have added that getters/setters enable to put breakpoints when data is read or written. – prapin Jul 29 '22 at 17:47
3

It's a methodology of OOP. It cam from different culture that predates modern languages. It even predates C++. The idea is that you can hide how data is stored and accessed within those member functions.

Note that in C++ functions that are declared within class are considered inline. So writing

 thing a;
 a.setMarbles(4);

most likely generates same code as

 thing a;
 a.marbles = 4;

On other hand, if your marbles in future will be accessed from several threads, you easily can fix code by adding synchronization or change marbles to atomic by changing only thing class. In second case you have to fix every occurrence in your code.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Wouldn't making it inline destroy the whole purpose of it since changes to the function won't be applied to old code using it? – sammonius Jul 29 '22 at 19:51
  • 2
    @sammonius Depends. If your class is in a library, the client code would have to be recompiled, yes. But at least it doesn't require any changes in the dependent code. – HolyBlackCat Jul 29 '22 at 20:27
  • @HolyBlackCat That's what I meant. I probably should have clarified that my whole question was about libraries. – sammonius Jul 29 '22 at 20:29
  • 1
    @sammonius Different libraries assign different importance to binary compatibility. Not everyone cares about it. – HolyBlackCat Jul 29 '22 at 20:31
  • @Holyblackcat but in that case, having a front-end class with some implementation also is a breach of compatibility. For binary compatibility something special should be used, e.g. PIMPL. – Swift - Friday Pie Jul 29 '22 at 20:54
  • @sammonius designing a stand-alone public module used as a static or shared library is whole new barrel of fish compared to internal code. It does't come free and system-level languages like C and C++ allow very fine control over your main resource - cost of every operation. My example was to show that C++ allows exclude any cost of call in this case. If it's a shared library with binary compatibility requirement, then your class would't contain its implementation. Implementation would be hidden elsewhere – Swift - Friday Pie Jul 29 '22 at 20:55
3

If you later decide that you want to inject additional logic into getting/setting the value, those functions let you do it easily, without rewriting a bunch of code. This becomes proportionaly more important as your programs grow in size.

For example, you might want to check _marbles > 0 before changing the number.

If you're certain you'll never want this extra logic, yes, just make the field public.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207