35

This is a rather old topic: Are setters and getters good or evil?

My question here is: do compilers in C++ / D / Java inline the getters and setter?

To which extent do the getters/setters impact performance (function call, stack frame) compared to a direct field access. Besides all the other reasons for using them, I would like to know whether they are supposed to affect the performance besides being a good OOP practice.

alvatar
  • 3,340
  • 6
  • 38
  • 50
  • 1
    getters() and setters() are more prevalent in Java than C++. Though they exist the style of C++ makes the use of getters and setters not as relavant as in Java. The reason they happen more in Java is because of reflection and the need to get member objects out ofa bigger construct while using reflection (this happens a lot while streaming). – Martin York Jul 10 '09 at 16:34
  • 1
    You should code for human readability and clarity first. Then if there's a performance issue worry about optimization. – Steve Kuo Jul 10 '09 at 17:40

10 Answers10

26

It depends. There is no universal answer that is always going to be true.

In Java, the JIT compiler will probably inline it sooner or later. As far as I know, the JVM JIT compiler only optimizes heavily used code, so you could see the function call overhead initially, until the getter/setter has been called sufficiently often.

In C++, it will almost certainly be inlined (assuming optimizations are enabled). However, there is one case where it probably won't be:

// foo.h
class Foo {
private:
  int bar_;

public:
  int bar(); // getter
};

// foo.cpp
#include "foo.h"

int Foo::bar(){
  return bar_;
}

If the definition of the function is not visible to users of the class (which will include foo.h, but won't see foo.cpp), then the compiler may not be able to inline the function call.

MSVC should be able to inline it if link-time code generation is enabled as an optimization. I don't know how GCC handles the issue.

By extension, this also means that if the getter is defined in a different .dll/.so, the call can not be inlined.

In any case, I don't think trivial get/setters are necessarily "good OOP practice", or that there are "all the other reasons for using them". A lot of people consider trivial get/setters to 1) be a sign of bad design, and 2) be a waste of typing.

Personally, it's not something I get worked up about either way. To me, for something to qualify as "good OOP practice", it has to have some quantifiable positive effects. Trivial get/setters have some marginal advantages, and some just as insignificant disadvantages. As such, I don't think they're a good or bad practice. They're just something you can do if you really want to.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • You are right about trivial getters/setters... there are are arguments in both directions. Personally they feel like they flatten interfaces, which is nice. On the other hand cluttering the code with them is clearly a bad sign. – alvatar Jul 10 '09 at 15:34
  • "If the definition of the function is not visible to users of the class (which will include foo.h, but won't see foo.cpp), then the compiler may not be able to inline the function call." If the user only includes foo.h then there is absolutely no way whatsoever the compiler can generate inline code for it. – Troubadour Jul 10 '09 at 23:42
  • 1
    Sure is. Like I said, if you enable link-time code generation in MSVC, it'll do exactly what it says on the box, postpone code generation until link-time, at which point the definition *is* visible, and so it can be inlined. :) – jalf Jul 11 '09 at 00:26
  • A trivial getter() without setter() is a way to ensure read-only access for code outside the class and so it would be nice to know that it gets optimised away. – nyholku Jul 29 '19 at 05:57
9

In D, all methods of classes, but not structs, are virtual by default. You can make a method non-virtual by making either the method or the whole class final. Also, D has property syntax that allows you to make something a public field, and then change it to a getter/setter later without breaking source-level compatibility. Therefore, in D I would recommend just using public fields unless you have a good reason to do otherwise. If you want to use trivial getters/setters for some reason such as only having a getter and making the variable read-only from outside the class, make them final.


Edit: For example, the lines:

S s;
s.foo = s.bar + 1;

will work for both

struct S
{
     int foo;
     int bar;
}

and

struct S
{
     void foo(int) { ... }
     int bar() { ... return something; }
}
BCS
  • 75,627
  • 68
  • 187
  • 294
dsimcha
  • 67,514
  • 53
  • 213
  • 334
  • 1
    THanks for the D-specific answer. I'm very interested on this. Could you point me to an example of turning a public field into getter/setter without breaking source-level compatibility, please? Thanks a lot! :) – alvatar Jul 10 '09 at 15:57
  • 3
    It's not actually supported that well. Basically, if you have a int setFoo(int) you can call it like setFoo = 4;, and you can omit the brackets on parameter-less function calls; but you can't use more complex operators, like ++ and +=. – FeepingCreature Jul 10 '09 at 16:08
  • 2
    About the virtuals: Another way to make a method non virtual is to change "void foo()" into void foo()()". This makes it a templated method that accepts no template params and can be implicitly instantiated too. Templates are never virtual. – Tim Matthews Jul 10 '09 at 22:03
  • Great! Thanks for the example. Maybe the support for more complex operators should be proposed for D2. – alvatar Jul 11 '09 at 04:11
6

I tried in Java: the same thing with and without getter and setter. The result: there is no significant difference between the execution time of the two versions. Here' s the code:

class Person
{
    public int age;
    String name;
    public Person(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }

}
class GetSetPerson
{
    private int age;
    String name;
    public GetSetPerson(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }
    public void setAge(int newage)
    {
        age=newage;
    }
    public int getAge()
    {
    return age; 
    }
}
class Proba
{
//Math.hypot kb 10-szer lassabb, mint a Math.sqrt(x*xy*y)!!!

public static void main(String args[])
{
long startTime, endTime, time;
int i;int agevar;
//Person p1=new Person("Bob",21);
GetSetPerson p1=new GetSetPerson("Bob",21);
startTime=System.nanoTime();
/*
for (i=0;i<1000000000;i++)
     {
     p1.age++;

     }

*/    
for (i=0;i<1000000000;i++)
     {
     agevar=p1.getAge();
     agevar++;
     p1.setAge(agevar);
     }

endTime=System.nanoTime();
time=endTime-startTime;
System.out.println(""+time);
System.out.println(p1.name+"'s age now  is "+p1.getAge());
}

}

I know, at the end Bob is a bit older than me, but for this, it should be okay.

Arpad Kosa
  • 321
  • 3
  • 5
5

I had exactly the same question once.

To answere it I programmed two small Programs:

The first:

#include <iostream>

class Test
{
     int a;
};

int main()
{
    Test var;

    var.a = 4;
    std::cout << var.a << std::endl;
    return 0;
}

The second:

#include <iostream>

class Test
{
     int a;
     int getA() { return a; }
     void setA(int a_) { a=a_; }
};

int main()
{
    Test var;

    var.setA(4);
    std::cout << var.getA() << std::endl;
    return 0;
}

I compiled them to assembler, with -O3 (fully optimized) and compared the two files. They were identical. Without optimization they were different.

This was under g++. So, to answere your question: Compilers easily optimize getters and setters out.

Marenz
  • 2,722
  • 3
  • 20
  • 19
  • Yeah that's the test to make :). I asked also for the general overview of this problem, with more compilers and languages. but definitely helpful. Thanks! – alvatar Jul 11 '09 at 04:14
4

Any JVM (or compiler) worth its salt needs to support inlining. In C++, the compiler inlines the getters and setters. In Java, the JVM inlines them at runtime after they have been called "enough" times. I don't know about D.

notnoop
  • 58,763
  • 21
  • 123
  • 144
4

Depending on the expected evolution of your class, get/setters may clutter your code vs. give you the flexibility to extend your implementation without affecting the client code.

Often I encounter classes that are used as 'data containers', which have both getter and setter for each member. That's nonsense. If you don't expect to need some 'special' functionality to be triggered when getting or setting a member, don't write it.

Given the speed of the machine, the effort of writing the extra abstraction will cost your clients more money.

Most compilers can optimize trivial getters/setters, but as soon as you declare them virtual (in C++), you pay an extra lookup - often worth the effort.

xtofl
  • 40,723
  • 12
  • 105
  • 192
3

A getter would be required for certain members in some cases. However providing a getter and setter for each data member of a class is not a good practice. Doing that would be complicating the class's interface. Also setters would bring in resource ownership issues if not handled properly.

Modicom
  • 299
  • 2
  • 4
3

Quoting from here Regarding the D Programming Langiage

I think DMD is currently unable to de-virtualize virtual getters and setters. Virtual calls are a bit slower by itself, but they also don't allow inlining, so successive standard optimizations can't be done. So if such accesses to the attribute is a virtual call and this happens in a "hot" part of the code, then it may slow down your code significantly. (if it happens in non-hot parts of the code it has usually no effects. That's why Java Hot Spot doesn't need optimize all your code to produce a very fast program anyway).

I have encouraged Frits van Bommel to improve the devirtualization capabilities of LDC:

Now LDC is able to do that in few very simple situations, but most times the situation is unchanged compared to DMD. Eventually LLVM will improve, so this situation can improve by itself. But the front-end too may do something about this.

Here is some documentation about this topic, some older, some more modern:

Community
  • 1
  • 1
BCS
  • 75,627
  • 68
  • 187
  • 294
2

In C++, when a compiler's optimizations are enabled then getters and setters may be 'inlined': i.e., be implemented using the same machine instructions as there would be for direct access to the underlying member data.

ChrisW
  • 54,973
  • 13
  • 116
  • 224
1

I'll echo Xtofl.

Getters and setters are one of those things that are sometimes useful, but you get these doctrinaire people who somehow leap from "has proven useful in some cases" to "must be used all the time and if you don't use it you are a heretic who should be killed".

If you have side effects to a get or set, by all means use a getter or setter.

But if not, writing getters and setters just clutters up the code. There's a small performance penalty. In Java, if it's only executed a few times, the perforance penalty shouldn't matter, and if it's executed frequently, it should be inlined so the penalty is mitigated. So I'd worry more about making the code harder to read.

And don't for a minute buy the argument that this eliminates the problem of global data. Global data is bad and should be avoided. But declaring a member field private and then creating public getters and setters for does nothing to solve the problem.

Jay
  • 26,876
  • 10
  • 61
  • 112
  • 1
    Some variables just shouldn't be exposed by themselves. The ones that should should go through getters and setters. Unless we're talking about something like a C struct conceptually, and unlike a class. – David Thornley Jul 10 '09 at 17:20