Are get and set functions popular with C++ programmers?


Question

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 Earwicker.
    void SetBar(std::string bar);
private:
    std::string bar;
}

const std::string Foo::GetBar()
{
    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.

1
32
5/23/2017 12:18:27 PM

Accepted Answer

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.

34
7/5/2017 8:43:01 PM

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.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon