Why do people write private-field getters returnin

2019-01-26 05:43发布

问题:

We can all agree on public variables being bad for encapsulation and all that. However, I noticed a lot of code that does this type of stuff:

class foo {
private:
    int integer_;
    string someString_;
    // other variables
public:
    int& integer() { return integer_; }
    string& someString() { return someString_; }
    // other "functions"
}

int main() {
    foo f;
    f.integer() = 10;
    f.someString() = "something";
    return 0;
}

I have seen this being used in many places and I don't get why. Basically it returns a reference to the data and thus exposes it directly to the outside. So encapsulation is not really achieved, not from any perspective.

Why is this commonly used?

回答1:

There's a recurring mantra, that getter/setter functions should be used to encapsulate your data. Hence many (unexperienced or coffee-overloaded) programmers get the idea they should use something like:

int& integer() { return integer_; }

but that isn't much different from simply writing:

class foo {
public: // <<<
    int integer_;
    string someString_;
    // ...
};

Well, it adds a function call, but you cannot control what the client does with the reference.


If you really want to provide a getter function write:

const int& integer() const { return integer_; }

A corresponding setter function looks like:

void integer(const int& value) {
    integer_ = value;
}


回答2:

I have to partially disagree both with @πάνταῥεῖ and @Rakete1111 's answers, considering how a class's definition is something that may evolve over time.

While it's true that, often, these getter methods are written by someone who's just heard the "no exposing members" mantra, they can also have legitimate uses:

  1. The getter method may later be modified to include some kind of validity check or resource allocation code before returning the reference - which direct access to the data member does not allow. While this means changing the class's code, it does not require changing the class user code. Also, if the implementation of the getter is not exposed in the class header, it might not even require recompiling the class user code. Note: Doing so is probably a sign of some other poor design choice.
  2. The getter method may be overridden by a subclass (in which case it is often made a virtual method) in a similar way to the above.
  3. The getter method may later replace its return type with a proxy for the original data member type, rather than a reference to an actual member - which may no longer even exist. Think of how vector<bool> works; when you call its operator[] you don't get a boolean&, you get some kind of proxy which, when assigned or assigned-to, does the appropriate bit extraction or setting.
  4. A non-const getter is not usable for non-const instances. So actually it does limit access relative to exposing the member outright. Whether the author of OP's example class actually intended this is a different question...

To sum up: The "dummy" non-const-reference getter can be a stub for other, meaningful, code.

That being said, it is often a good idea to just make the getter return a const reference or a value. Or just exposing the field in those cases where it's appropriate (and there are some of those too).



回答3:

I would strongly discouraged returning a non-const reference to a private variable. Not because it breaks encapsulation, but because it is unnecessary: Why not make the variable public in the first place?

Breaking encapsulation is bad, yes, but that does not mean that every variable should be private. Some variables are meant to be read and modified from the user, so it would make sense to make them public. For example, take std::pair, it has 2 public member variables, first and second. That's not bad practice.

The only time it doesn't make sense is when the variable is not supposed to be written to. That would be bad, as it would actually break encapsulation and make the whole program hard to debug.



回答4:

This construction may be used for debugging purposes.

If you have a public variable, you can't monitor its usage easily. Converting it to a pair of private variable and method-returning reference will allow you to put a breakpoint and/or log the calls.

However, separate getter and setter would serve the same purpose even better, so this is just an advantage over plain public variables.



回答5:

I wrote this once. I planned later to go back and replace the field getter with something that returned a stack object to something that could be cast to the original type and assigned to by the original type. This allowed me to go back later and intercept all assignments to put validations in.

Kinda overpowered techinque. None of the other coders on the project could understand it at all. The whole stack got ripped out.