Conventions for accessor methods (getters and setters) in C++

C++AccessorSetterGetter

C++ Problem Overview


Several questions about accessor methods in C++ have been asked on SO, but none was able satisfy my curiosity on the issue.

I try to avoid accessors whenever possible, because, like Stroustrup and other famous programmers, I consider a class with many of them a sign of bad OO. In C++, I can in most cases add more responsibility to a class or use the friend keyword to avoid them. Yet in some cases, you really need access to specific class members.

There are several possibilities:

1. Don't use accessors at all

We can just make the respective member variables public. This is a no-go in Java, but seems to be OK with the C++ community. However, I'm a bit worried about cases were an explicit copy or a read-only (const) reference to an object should be returned, is that exaggerated?

2. Use Java-style get/set methods

I'm not sure if it's from Java at all, but I mean this:

int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount

3. Use objective C-style get/set methods

This is a bit weird, but apparently increasingly common:

int amount(); // Returns the amount
void amount(int amount); // Sets the amount

In order for that to work, you will have to find a different name for your member variable. Some people append an underscore, others prepend "m_". I don't like either.

Which style do you use and why?

C++ Solutions


Solution 1 - C++

From my perspective as sitting with 4 million lines of C++ code (and that's just one project) from a maintenance perspective I would say:

  • It's ok to not use getters/setters if members are immutable (i.e. const) or simple with no dependencies (like a point class with members X and Y).

  • If member is private only it's also ok to skip getters/setters. I also count members of internal pimpl-classes as private if the .cpp unit is smallish.

  • If member is public or protected (protected is just as bad as public) and non-const, non-simple or has dependencies then use getters/setters.

As a maintenance guy my main reason for wanting to have getters/setters is because then I have a place to put break points / logging / something else.

I prefer the style of alternative 2. as that's more searchable (a key component in writing maintainable code).

Solution 2 - C++

  1. is the best IMO, because it makes your intentions clearest. set_amount(10) is more meaningful than amount(10), and as a nice side effect allows a member named amount.

Public variables is usually a bad idea, because there's no encapsulation. Suppose you need to update a cache or refresh a window when a variable is updated? Too bad if your variables are public. If you have a set method, you can add it there.

Solution 3 - C++

  1. I never use this style. Because it can limit the future of your class design and explicit geters or setters are just as efficient with a good compilers.

    Of course, in reality inline explicit getters or setters create just as much underlying dependency on the class implementation. THey just reduce semantic dependency. You still have to recompile everything if you change them.

  2. This is my default style when I use accessor methods.

  3. This style seems too 'clever' to me. I do use it on rare occasions, but only in cases where I really want the accessor to feel as much as possible like a variable.

I do think there is a case for simple bags of variables with possibly a constructor to make sure they're all initialized to something sane. When I do this, I simply make it a struct and leave it all public.

Solution 4 - C++

  1. That is a good style if we just want to represent pure data.

  2. I don't like it :) because get_/set_ is really unnecessary when we can overload them in C++.

  3. STL uses this style, such as std::streamString::str and std::ios_base::flags, except when it should be avoided! when? When method's name conflicts with other type's name, then get_/set_ style is used, such as std::string::get_allocator because of std::allocator.

Solution 5 - C++

In general, I feel that it is not a good idea to have too many getters and setters being used by too many entities in the system. It is just an indication of a bad design or wrong encapsulation.

Having said that, if such a design needs to be refactored, and the source code is available, I would prefer to use the Visitor Design pattern. The reason is:

> a. It gives a class an opportunity to > decide whom to allow access to its > private state > >b. It gives a class an > opportunity to decide what access to > allow to each of the entities who are > interested in its private state > >c. It > clearly documents such exteral access > via a clear class interface

Basic idea is:

> a) Redesign if possible else, > > b) > Refactor such that >
> 1. All access to class state is via a well known individualistic > interface >
> > 2. It should be possible to configure some kind of do's and don'ts > to each such interface, e.g. all > access from external entity GOOD > should be allowed, all access from > external entity BAD should be > disallowed, and external entity OK > should be allowed to get but not set (for example)

Solution 6 - C++

  1. I would not exclude accessors from use. May for some POD structures, but I consider them a good thing (some accessors might have additional logic, too).

  2. It doesn't realy matters the naming convention, if you are consistent in your code. If you are using several third party libraries, they might use different naming conventions anyway. So it is a matter of taste.

Solution 7 - C++

I've seen the idealization of classes instead of integral types to refer to meaningful data.

Something like this below is generally not making good use of C++ properties:

struct particle {
    float mass;
    float acceleration;
    float velocity;
} p;

Why? Because the result of p.mass*p.acceleration is a float and not force as expected.

The definition of classes to designate a purpose (even if it's a value, like amount mentioned earlier) makes more sense, and allow us to do something like:

struct amount
{
    int value;

    amount() : value( 0 ) {}
    amount( int value0 ) : value( value0 ) {}
    operator int()& { return value; }
    operator int()const& { return value; }
    amount& operator = ( int const newvalue )
    {
        value = newvalue;
        return *this;
    }
};

You can access the value in amount implicitly by the operator int. Furthermore:

struct wage
{
    amount balance;

    operator amount()& { return balance; }
    operator amount()const& { return balance; }
    wage& operator = ( amount const&  newbalance )
    {
        balance = newbalance;
        return *this;
    }
};

Getter/Setter usage:

void wage_test()
{
    wage worker;
    (amount&)worker = 100; // if you like this, can remove = operator
    worker = amount(105);  // an alternative if the first one is too weird
    int value = (amount)worker; // getting amount is more clear
}

This is a different approach, doesn't mean it's good or bad, but different.

Solution 8 - C++

An additional possibility could be :

int& amount();

I'm not sure I would recommend it, but it has the advantage that the unusual notation can refrain users to modify data.

str.length() = 5; // Ok string is a very bad example :)

Sometimes it is maybe just the good choice to make:

image(point) = 255;  

Another possibility again, use functional notation to modify the object.

edit::change_amount(obj, val)

This way dangerous/editing function can be pulled away in a separate namespace with it's own documentation. This one seems to come naturally with generic programming.

Solution 9 - C++

Let me tell you about one additional possiblity, which seems the most conscise.

Need to read & modify

Simply declare that variable public:

class Worker {
public:
	int wage = 5000;
}

worker.wage = 8000;
cout << worker.wage << endl;

Need just to read

class Worker {
	int _wage = 5000;
public:
	inline int wage() {
		return _wage;
	}
}

worker.wage = 8000; // error !!
cout << worker.wage() << endl;

The downside of this approach is that you need to change all the calling code (add parentheses, that is) when you want to change the access pattern.

Solution 10 - C++

variation on #3, i'm told this could be 'fluent' style

class foo {
  private: int bar;
  private: int narf;
  public: foo & bar(int);
  public: int bar();
  public: foo & narf(int);
  public: int narf();
};

//multi set (get is as expected)
foo f; f.bar(2).narf(3);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNoarthView Question on Stackoverflow
Solution 1 - C++Just another metaprogrammerView Answer on Stackoverflow
Solution 2 - C++AshleysBrainView Answer on Stackoverflow
Solution 3 - C++OmnifariousView Answer on Stackoverflow
Solution 4 - C++SadeqView Answer on Stackoverflow
Solution 5 - C++ChubsdadView Answer on Stackoverflow
Solution 6 - C++Cătălin PitișView Answer on Stackoverflow
Solution 7 - C++gusView Answer on Stackoverflow
Solution 8 - C++log0View Answer on Stackoverflow
Solution 9 - C++Rok KraljView Answer on Stackoverflow
Solution 10 - C++user13985481View Answer on Stackoverflow