Are accessors (get and set functions) popular with C++ programmers?
Asked Answered
S

14

53

I'm from the world of C# originally, and I'm learning C++. I've been wondering about get and set functions in C++. In C# usage of these are quite popular, and tools like Visual Studio promote usage by making them very easy and quick to implement. However, this doesn't seem to be the case in the C++ world.

Here's the C# 2.0 code:

public class Foo
{
    private string bar;

    public string Bar
    {
        get { return bar; }
        set { bar = value; }
    }
}

Or, in C# 3.0:

public class Foo { get; set; }

May people will say, well whats the point in that? Why not just create a public field and then make it a property later if you need to; honestly, I'm actually not sure. I just do it out of good practice because I've seen it done so many times.

Now because I'm so used to doing it, I feel like I should carry over the habit to my C++ code, but is this really necessary? I don't see it done as often as with C#.

Anyway, here's the C++ from what I gather:

class Foo
{
public:
    std::string GetBar() const; // Thanks for the tip, @Daniel Earwicker.
    void SetBar(std::string bar);
private:
    std::string bar;
}

std::string Foo::GetBar() const
{
    return bar;
}

void Foo::SetBar(std::string bar)
{
    // Also, I always wonder if using 'this->' is good practice.
    this->bar = bar;
}

Now, to me that seems like a whole lot of leg work; considering using Visual Studio's tools the C# implementation would take literally seconds to implement, and the C++ took me a lot longer to type - I feel its not worth the effort, especially when the alternative is 5 lines long:

class Foo
{
public:
    std::string Bar;
}

From what I gather, these are the advantages:

  • You can change implementation details for the get and set functions, so instead of returning a private field you can return something more interesting.
  • You can remove a get/set later on and make it read/write only (but for a public facing interface, this seems, not good).

And the disadvantages:

  • Takes ages to type, is this really worth the effort? Generally speaking. In some cases, the advantages make it worth the effort, but I mean, speaking in terms of "good practice", is it?

Answer:

Why did I choose the answer with less votes? I was actually very close to choosing veefu's answer; however my personal opinion (which is apparently controversial), is that the answer over egged the pudding.

The answer I chose, on the other hand, seems to argue both sides; I think getters and setters are evil if used excessively (by that I mean, when it's not necessary and would break the business model), but why shouldn't we have a function called GetBalance()?

Surely this would be far more versatile than PrintBalance(); what if I wanted to show it to the user in another way than as the class wanted me to? Now, in some sense GetBalance() may not be relevant enough to argue that "getters and setters are good" because it doesn't (or maybe, shouldn't) have an accompanying setter, and speaking of which, a function called SetBalance(float f) could be bad (in my opinion) because it would imply to the implementer of the function that the account must be manipulated out side of the class, which is not a good thing.

Sputnik answered 10/4, 2009 at 11:56 Comment(3)
Note that when passing a std::string into a method, you should use <code>const std::string&</code> rather than plain <code>std::string</code>, to avoid an unneccessary copy; this is a bit of a gotcha if you're coming from a C#/Java background.Gayn
I don't like using this-> to access members. I prefer to prefix all members with m_. It has the same effect, let the reader know where the variable came from.Illsuited
Caspin: Except that this-> is guaranteed to work no matter naming convention. There's nothing to stop me from creating a local variable or a function parameter with the m_ prefix. ;) And the this-> prefix has the further advantage that I can skip it when it's not necessary. :)Chaldean
H
52

I'd argue that providing accessors are more important in C++ than in C#.

C++ has no builtin support for properties. In C# you can change a public field to a property mostly without changing the user code. In C++ this is harder.

For less typing you can implement trivial setters/getters as inline methods:

class Foo
{
public:
    const std::string& bar() const { return _bar; } 
    void bar(const std::string& bar) { _bar = bar; } 
private:
    std::string _bar;
};

And don't forget that getters and setters are somewhat evil.

Hampton answered 10/4, 2009 at 12:8 Comment(2)
In some ways, it's better to return a copy and not to return a const ref. This allows Foo's implementation to change the internal representation of bar at some point in the future. The downside is that it's more costly in time and space to return a copy.Falsehood
@Artyom: you're correct, i've fixed that. @Marin: returning a const reference creates a very strong coupling. It makes very hard to change the underlying representation.Hampton
G
40

At the risk of being argumentative, I'll back an opposing point of view I first encountered while reading "Holub on Patterns". It was a point of view that was very challenging, but made sense to me upon reflection:

Getters and Setters are Evil

Use of getters and setters is in opposition to the fundamentals of object oriented design: Data abstraction and encapsulation. Overuse of getters and setters will make your code less agile and maintainable in the long run. They ultimately expose the underlying implementation of your class, locking implementation details into the interface of the class.

Imagine your 'std::string Foo::bar' field needs to change from a std::string to another string class, that, say, is better optimized or supports a different character-set. You'll need to change the private data field, the getter, the setter, and all the client code of this class that calls these getters and setters.

Rather than design your classes to "provide data" and "receive data", design them to "perform operations" or "providide services". Ask yourself why you're writing a "GetBar" function. What are you doing with that data? Perhaps you're displaying that data on or doing some processing on it. Is this process better exposed as a method of Foo?

This not to say that getters and setters don't have their purpose. In C# I believe the fundamental reason for their use is to interface with the Visual Studio GUI-design IDE, but if you find yourself writing them in C++, it's probably best to take a step back, look at your design, and see if something is missing.

I'll try to mock-up an example to illustrate.

// A class that represents a user's bank account
class Account {
  private:
    int balance_; // in cents, lets say 
  public:
    const int& GetBalance() { return balance_; }
    void SetBalance(int b) { balance_ = b; }
};

class Deposit {
  private:
    int ammount_;
  public:
    const int& GetAmount() { return ammount_; }
    void SetAmmount(int a) { _balance = a; }
};

void DoStuffWithAccount () {
  Account a;
  // print account balance
  int balance = a.GetBalance();
  std::cout << balance;

  // deposit some money into account
  Deposit d(10000);
  a.SetBalance( a.GetBalance() + d.GetValue());
}

It doesn't take very long to see that this is very poorly designed.

  1. Integers are an awful currency datatype
  2. A Deposit should be a function of the Account

The getters and setters make it more difficult to fix the problems, since the client code DoStuffWithAccount is now bound to the data-type we used to implement the account balance.

So, lets make a pass on this code and see what we can improve

// A class that represents a user's bank account
class Account {
  private:
    float balance_;
  public:
    void Deposit(float b) { balance_ += b; }
    void Withdraw(float w) { balance_ -= w; }
    void DisplayDeposit(std::ostream &o) { o << balance_; }
};

void DoStuffWithAccount () {
  Account a;
  // print account balance
  a.DisplayBalance(std::cout);

  // deposit some money into account
  float depositAmt = 1000.00;
  a.Deposit(depositAmt);
  a.DisplayBalance(std::cout);
}

The 'float' is a step in the right direction. Granted, you could have changed the internal type to 'float' and still supported the getter/setter idiom:

class Account {
  private:
    // int balance_; // old implementation
    float balance_; 
  public:
    // support the old interface
    const int& GetBalance() { return (int) balance_; }
    void SetBalance(int b) { balance_ = b; }
    // provide a new interface for the float type
    const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int??
    void SetBalance(float b) { balance_ = b; }
};

but it doesn't take long to realize that the getter/setter arrangement is doubling your workload and complicating matters as you need to support both the code that used ints and the new code that will use floats. The Deposit function makes it a bit easier to expand the range of types for depositing.

An Account-like class is probably not the best example, since "getting" the account balance is a natural operation for an Account. The overall point, though, is that you must be careful with getters and setters. Do not get into the habit of writing getters and setters for every data-member. It is quite easy to expose and lock yourself into an implementation if you are not careful.

Glucoside answered 10/4, 2009 at 13:2 Comment(19)
Hmm, good answer, but very abstract so I'm finding it hard to relate to when you say "and see if something is missing." - could you perhaps think of an example? Much appreciated.Sputnik
Well, why do you need to get/set values constantly? Shouldn't the object be initialized to a valid state in the constructor? Why do you need to get the value? Are you doing something with it that the object itself could/should be responsible for?Chaldean
Getters/Setters for every data member are evil because they expose the implementation, and can constrain your subsequent choice of a different implementation. Getting/Setting properties of the abstraction is appropriate, and moreover, hides the implementation, and is a Good Thing (tm).Sergiosergipe
I strongly dislike your example. The fact of a balance, and the power to check it are at the core of the idea "account" (a direct setter would break the security model, natch). These are necessary parts of the abstraction and are not necessarily connected to transactions on the account.Sergiosergipe
I disagree. A pure 'getter' still locks you into the data model you've chosen breeds inflexibility. What are you doing when you "check an account balance"? If you're displaying it in some way, add 'display' or string conversion or stream insertion methods. If you have a better example, please share.Glucoside
Don't know what "pure" means here, but const Currency& Balance() const (in C++ idiom) does not lock in my implementation: a naive implementation it just returns a data member, but this is not required and can be changed at a later date. And display is certainly not the job of the modeling layer.Sergiosergipe
If the Currency object you're returning is quite flexible and extensible without breaking existing clients, then sure, you're probably fine. Do you have anything constructive to add or you want to continue shooting at my straw-man?Glucoside
Point::moveTo(new_x, new_y), Point::moveTo(new_Point), Point::strafeTo(new_x), and Point::riseTo(new_y) are more poetic than Point::setX(new_x) and Point::setY(new_y).Granthem
+1 There are some cases when you need getters/setters (a Dialog class, maybe?) but those are rare.Jobber
veefu: You need only cede the detail. You're right on the broad sweep, and I like the edit, I was being pedantic. Perhaps needlessly so.Sergiosergipe
Umm.. getters and setters allwo to change the implementation without breaking the interface. They won't fix a bad interface, but don't blame a valid tool for its abuse.Ankylostomiasis
No one is blaming the tool for its abuse. This post is simply pointing out that the tool is seemingly designed only for interfaces that are broken in the first place. It's not that getters/setters are harmful, they just shouldn't be necessary in a well-designed class.Chaldean
Hmm, I'm still unsure about this; I agree totally that you shouldn't religiously use getters and setters for all private members without question, this would clearly be moronic. However, I disagree partially because a GetBalance() function would be useful, however I agree that SetBlance() would not.Sputnik
What?! Please, DO NOT USE FLOATS FOR CURRENCY. Float and double data types are not suitable for precise values, such as currency. Read any programming book, or java.sun.com/docs/books/tutorial/java/nutsandbolts/… .Fannie
Juliano, my purpose was not to demonstrate the best implementation of an account, but to demonstrate how getters and setters may make re-implementation more difficult. You see, by exposing with getters and setters, following your (very good) advice is more difficult.Glucoside
Yes, sure, I get your point, but you should be careful with what you say. Newbies may read your answer and get the wrong idea that floats can be used for currency. If it is just for example, replace floats with an hypothetical "Currency" class. That is less harmful than using float as an example.Fannie
@veefu, @Juliano: Juliano has a point, but perhaps it would be more helpful to direct newbies to a helpful question (#149533), than to flame the author of a perfectly good (if not great) answer.Sputnik
Thanks for the link r3n. I didn't consider it a flame at all, just sound advice.Glucoside
I agree float should be take out of answer. Discredits it to me. Wny listen to someone who is using a float for currency and specifically recommends it as being superior to an integer.Dumont
S
12

In your example:

class Foo
{
public:
    const std::string GetBar(); // Should this be const, not sure?

You probably mean this:

std::string GetBar() const;

Putting the const at the end means "This function doesn't modify the Foo instance it is called on", so in a way it marks it as a pure getter.

Pure getters occur frequently in C++. An example in std::ostringstream is the str() function. The Standard library often follows a pattern of using the same function name for a pair of getter/setter functions - str being an example again.

As to whether it's too much work to type out, and is it worth it - that seems an odd question! If you need to give clients access to some information, provide a getter. If you don't, then don't.

Sprout answered 10/4, 2009 at 12:2 Comment(14)
Re: "that seems an odd question" - Hmm, yes, I felt like I may look a little lazy by saying this; but I was worried about it being pointless and making a fool of myself later in life hehe.Sputnik
On the whole, don't waste time exposing anything from a class until you find you need it (and always do so grudgingly!) And by the way, almost everything is hard work in C++ compared to C# - but usually for good reasons, given the specific aims of the language.Sprout
Yes, I've noticed the increased workload, much more typing. What if the field really needs to be get and set, and initially it's only going to be used in your own library, but may become public facing in future?Sputnik
In C++, if a member variable is truly just a storage slot with no added behaviour, then you can just make it public (there's a reason to not do this in C#, but it doesn't apply in C++). But it's actually very unusual for this to be truly required in a good class design.Sprout
Exposing data members sets you up for grief latter if the implementation changes. -- The Voice of Bitter ExperienceSergiosergipe
I remember once when having getters and setters instead of public members saved my ass. I don't recommend having them, but (outside of POD structs) public data is evil.Electrostatic
@dmckee - of course, but a pair of trivial public getter/setters is not any different from a public member - the same operations are exposed (with only minor complications depending on whether or not the getter returns a non-const reference).Sprout
@David Thornley - how did it save your ass?Sprout
To be absolutely clear, I'm recommending NEITHER public data NOR unnecessary getter/setter pairs.Sprout
@Earwicker: A getter (at least on that returns a constant or constant reference) can be changed to return a computed value without requiring changes in the client code. Done this at least once. If the computation is invertible, setters might be likewise reimplemented. Don't recall doing that. YMMV.Sergiosergipe
Yes, that happens a lot for me too, but not if it also has a setter. An example would be 'first' and 'second' in 'std::pair' - you can assign to them, and you can read them, so clients will expect to read back whatever they stored. Changing that would invalidate the implicit "contract" of get/set.Sprout
Although you can do an imperfect job of simulating properties in C++ - I posted an answer about it here a while back but I can't find it now.Sprout
@Earwicker: I had to take some action when a certain value changed. I put a bit of code in every member function that changed it, including the setter. If it had been a public member, I would have had immense difficulty making it work and maintaining it.Electrostatic
If it had been a public member, you could have replaced it with a member that defined the assignment operator to carry out the action when assigned to! :) (Or if it's all your own code, it's not that hard to fix up the calling code to call a method instead of assigning.)Sprout
A
6

[edit] It seems I need to emphasize that setters need to validate parameters and enforce invariants, so they are usually not as simple as they are here. [/edit]


Not with all, because fo the extra typing. I tend to use them much more often now that Visual Assist gives me "encapsulate field".

The legwork is not more if you implement just the default setters / getters inline in the class declaration (which I tend to do - more complex setters move to the body, though).

Some notes:

constness: Yes, the getter should be const. It is no use to make the return value const, though, if you return by value. For potentially complex return values you might want to use const & though:

std::string const & GetBar() const { return bar; } 

Setter chaining: Many developers like to modify the setter as such:

Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; }

Which allows calling multiple setters as such:

Foo foo;
foo.SetBar("Hello").SetBaz("world!");

It's not universally accepted as a good thing, though.

__declspec(property): Visual C++ provides this non-standard extension so that callers can use property syntax again. This increases legwork in the class a bit, but makes caller code much friendlier looking.


So, in conclusion, there's a little bit of more legwork, but a handful of decisions to make in C++. Typical ;)

Ankylostomiasis answered 10/4, 2009 at 12:9 Comment(4)
Re: "you might want to use const & though" - I'm not sure I understand why; I'm quite ropey on pointers and references, but I assume this returns a reference, and without it just returns a value? Perhaps you could expand on this briefly?Sputnik
this can avoid extra temporaries copies when passing the result as an argument to another function using a const& parameterOrnelas
@r3n - don't return a local variable by reference - the caller will get back a piece of junk with random ("undefined") behaviour.Sprout
@Earwicker: bar is an instance variable, not a local one. As long as the Foo object isn't a temporary, everything's fine.Falsehood
C
6

There is no really strict convention on this, like there is in C# or Java. Many C++ programmers would just make the variable public an save themselves the trouble.

As other answers have said, you shouldn't often need set, and to some extent, get methods.

But if and when you do make them, there's no need to type more than necessary:

class Foo
{
public:
    std::string Bar() const { return bar; }
    void Bar(const std::string& bar) { this->bar = bar; }
private:
    std::string bar;
};

Declaring the functions inline in the class saves typing, and hints to the compiler that you'd like the functions inlined. And it's not much more typing than the C# equivalents. One thing to note is that I removed the get/set prefixes. Instead, we just have two Bar() overloads. That's fairly common in C++ (after all, if it doesn't take any arguments, we know it's the getter, and if it takes an argument, it's the setter. We don't need the name to tell us that), and it saves a bit more typing.

Chaldean answered 10/4, 2009 at 13:18 Comment(2)
About your second sentence: the C++ programmers I know don't believe in public data members, except in POD structs.Electrostatic
Fair enough. Changed "most" to "many". I just wanted to emphasize that it's less dogmatic than in C#/JavaChaldean
O
5

I hardly ever use getters and setters in my own code. Veefu's answer looks good to me.

If you insist on having getters and/or setters, you can use macros to cut down on the boiler-plate.

#define GETTER(T,member) const T& Get##member() const { return member; }
#define SETTER(T,member) void Set##member(const T & value) { member = value; }

class Foo
{
public:
    GETTER(std::string, bar)
    SETTER(std::string, bar)
private:
    std::string bar;
}
Obregon answered 10/4, 2009 at 18:43 Comment(2)
This is really a good idea and nice practice? In C this most of the times turns out to be a mess. You're using something the way it was not design to use.Navelwort
@CaioV. I admit right up front that it's not a good idea. But sometimes goodness is relative. I'd prefer to see the macros rather than the code the macros expand out to.Obregon
S
3

Getting and setting data members qua data members: Bad.
Getting and setting elements of the abstraction: Good.

Sergiosergipe answered 10/4, 2009 at 16:8 Comment(0)
C
3

The arguments against Get/Set in terms of API design in the banking example are spot on. Dont expose fields or properties if they will allow users to break your business rules.

However, once you have decided that you do need a field or property, always use a property.

The automatic properties in c# are very easy to use, and there are many scenarios (databinding, serialization, etc) that do not work with fields, but require properties.

Convert answered 10/4, 2009 at 20:23 Comment(0)
C
1

If you are developing COM components then yes, it is very popular.

Colored answered 10/4, 2009 at 12:3 Comment(0)
C
1

get and set are a pain inflicted upon people if you have to use them in any language.

Eiffel has it alot better where all that differs is the amount of information you have to provide to get the answer - a function with 0 parms is the same as accessing a member variable, and you can change freely between them.

When you control both sides of an interface the definition of the interface doesn't seem like such a big issue. However when you want to change implementation details and it inflicts the recompilation of client code as is the common case in C++ you wish to be able to minimise this as much as possible. As such pImpl and get/set would get used more in public APIs to avoid such damage.

Colly answered 10/4, 2009 at 13:9 Comment(0)
D
1

Get and Set methods are useful if you have constraints in a variable value. For example, in many mathematical models there is a constraint to keep a certain float variable in the range [0,1]. In this case, Get, and Set (specially Set) can play a nice role:

class Foo{
public:
    float bar() const { return _bar; } 
    void bar(const float& new_bar) { _bar = ((new_bar <= 1) && (new_bar >= 0))?new_bar:_bar; } // Keeps inside [0,1]
private:
    float _bar;     // must be in range [0,1]
};

Also, some properties must be recalculated before reading. In those cases, it may take a lot of unnecessary computing time to recalculate every cicle. So, a way to optimize it is to recalculate only when reading instead. To do so, overload the Get method in order to update the variable before reading it.

Otherwise, if there is no need to validade input values, or update output values, making the property public is not a crime and you can go along with it.

Dayledaylight answered 17/3, 2015 at 13:54 Comment(0)
P
0

Yes , get and set are popular in the c++ world.

Parasitism answered 10/4, 2009 at 12:58 Comment(5)
Hmm, can anyone explain why someone would have voted -1 on this?Sputnik
I belive it's Because I said that it is mandatory , I didn't actualy ment that , What I ment by saying mandatory is that this is the right way to expose internal data.Parasitism
They're not popular where I hang around, because exposing internal data is normally a Very Bad Idea.Electrostatic
They're VERY unpopular with me. They show that a programmer doesn't actually understand object orientation.Entablature
@Rob K: Writing basic getters and setters for each member variable is a sure sign. However, in many classes, the contents of some variables are relevant by themselves (m_deposit in a bank account class, m_z in a vector class), so some good OO functions reduce to getters.Electrostatic
M
0

If you use C++/CLI as your varient of C++, then it has native property support in the language, so you can use

property String^ Name;

This is the same as

String Name{get;set;}

in C#. If you need more precise control over the get/set methods then you can use

property String^ Name
{
   String^ get();
   void set(String^ newName);
}

in the header and

String^ ClassName::Name::get()
{
   return m_name;
}

void ClassName::Name::set(String^ newName)
{
   m_name = newName;
}

in the .cpp file. I can't remember off hand, but I think you can have different access permissions for the get and set methods (public/private etc).

Colin

Mohican answered 21/4, 2009 at 12:17 Comment(0)
M
-3

The compiler will emit set_ and get_ if you define a property, so it's really just save some typing.

This has been an interesting discussion. This is something from my favorite book "CLR via C#".

Here is what I quoted.

Personally, I don't like properties and I wish that they were not supported in the Microsoftm.NET Framework and its programming languages. The reason is because properties look like fields but they are methods. This has been known to cause a phenomenal amount of confu-sion. When a programmer sees code that appears to be accessing a field, there are many assumptions that the programmer makes that may not be true for a property. For example,

  • A property may be read-only or write-only; field access is always
    readable and writable. If you define
    a property, it is best to offer both
    get and set accessor methods.
  • A property method may throw an exception; field access never throws
    an exception.

  • A property cannot be passed as an out or ref parameter to a method; a field can.

  • A property method can take a long time to execute; field access always
    completes imme- diately. A common
    reason to use properties is to
    perform thread synchronization, which can stop the thread forever, and
    therefore, a property should not be
    used if thread synchro- nization is
    required. In that situation, a method is preferred. Also, if your class can be accessed remotely (for example,
    your class is derived from
    System.MashalByRefObject), calling
    the property method will be very
    slow, and therefore, a method is
    preferred to a property. In my
    opinion, classes derived from
    MarshalByRefObject should never use
    properties.

  • If called multiple times in a row, a property method may return
    a different value each time; a
    field returns the same value each
    time. The System.DateTime class has a read- only Now property that returns
    the current date and time. Each time you query this property, it will
    return a different value. This is a
    mistake, and Microsoft wishes that
    they could fix the class by making
    Now a method instead of a property.

  • A property method may cause observable side effects; field access never does. In other words, a user of a type should be able to set various
    properties defined by a type in any
    order he or she chooses without
    noticing any different behavior in
    the type.

  • A property method may require additional memory or return a
    reference to something that is not
    actually part of the object's state, so modifying the returned object has
    no effect on the original object;
    querying a field always returns a
    reference to an object that is
    guaranteed to be part of the original object's state. Working with a
    property that returns a copy can be
    very confusing to developers, and
    this characteristic is frequently not documented.
Monochromatism answered 10/4, 2009 at 13:13 Comment(1)
The question was about C++ though. The C# code was just an example. :)Chaldean

© 2022 - 2024 — McMap. All rights reserved.