4

A. How useful/cumbersome is the following trick of using the same function for getter as well as setter, by returning a reference?

B. How good is the practice of adding const to the end of function declarations in case of getters and setters?

#include <iostream>

class A
{
  int varReadWrite_;
  int varReadOnly_;
  int varRestricted_;

public:
  A() : varReadOnly_(25) {}
  virtual ~A() {}

  int& varReadWrite() { return varReadWrite_; }
  int varReadOnly() { return varReadOnly_; }
  int varRestricted() { return varRestricted_; }
  void setVarRestricted(int i); //throwable

};

int main(int argc, char *argv[])
{
  A a;
  a.varReadWrite() = 45;
  std::cout << a.varReadOnly() << a.varReadWrite() << std::endl;
  return 0;
}

The reasons, why I chose this design was:

  1. ease of access of explicitly read-only or explicitly writable variables.
  2. the restricted (I dont know what else to call them), the variables, that require sanitization and filtering before being assigned -- these variables might require an explicit setter.

Using boost fusion map is also an interesting possibility as shown here

Update

Const Reference Members are interesting for read-only access to variables, e.g.

class A {
  int mA;
public:
  int& a;
  A(int a_ = 0) : mA(a_), a(mA) {}
};

Practically this comes with the extra effort to code the copy and move constructors, which is an acceptable compromise for me.

Cpp Reference Copy Construtor says

The implicitly-declared or defaulted copy constructor for class T is defined as deleted if... T has non-static data members that cannot be copied (have deleted, inaccessible, or ambiguous copy constructors);

Community
  • 1
  • 1
hell_ical_vortex
  • 361
  • 2
  • 11
  • 4
    It's as good/cumbersome as using a public member variable. – πάντα ῥεῖ Sep 26 '15 at 08:08
  • 2
    Returning a reference is usually not considered good practice, as users may hold on to it and you don't know when changes could happen, and it complicates lifetime issues. – sje397 Sep 26 '15 at 08:08
  • 1
    if no kind of change to the variable can break the class invariant, make it public. if not so, use proper methods that have descriptive names, do coherent changes, and uphold the class invariant. – sp2danny Sep 26 '15 at 08:47

2 Answers2

3

A. How useful/cumbersome is the following trick of using the same function for getter as well as setter, by returning a reference?

Returning a reference to your internal members in general is not recommended since this way you give an easy access to others so they could change your object internal state without using any method provided by the object's class API. Thus, it will be very difficult to track this kind of changes in the code. In general changes in the internal state of an object should only be possible through methods that belongs to the class API.

B. How good is the practice of adding const to the end of function declarations in case of getters and setters?

if you refer to adding const for methods like:

void PrintState() const

Then in general this doesn't make sense for setters. Const in this case means This method doesn't change the object state. So it's a commitment that you give to the caller to say: I will not change the object state by this call. In general it's very good practice since it helps you during the design to think about your methods and see which one is really modifying the object state or not. Additionally, it's a defensive programming since it's recursive: if you pass this object to some method by reference (through a pointer or reference) he can't call const methods unless this method is marked as const also. So this prevents from changing the object state by error.

rkachach
  • 16,517
  • 6
  • 42
  • 66
  • Thanks for a descriptive answer. This does give me insights into how I should approach specific programming tasks. I can't yet speak about designing a software, but I can sure bear it in my mind when I am there. – hell_ical_vortex Sep 27 '15 at 01:56
2

Accessors (a.k.a getters and setters) are as good/cumbersome as having public member variables, as you've just violated encapsulation and lied yourself. Mixing them in a single function is even worse, as the caller may hold the returned reference, opening the hole for even more subtle bugs than described in the link above.

Secondly, adding const to a member function declaration will protect you from setters, but not from getters. Anyway, plan your designs better off :).

Community
  • 1
  • 1
3442
  • 8,248
  • 2
  • 19
  • 41
  • Thanks for the link. That was insightful. I could not understand the design modifications suggested for the plain old structs. And also about what is considered bad/dangerous/dirty about their use in template meta-programming. Is it okay to ask here or should I open a new thread. – hell_ical_vortex Sep 27 '15 at 02:12
  • 1
    @bvraghav: It's okay here, because it happens to be about one of my previous answers. What I tryied to say about POSs (Plain-Old-Structs) is that they should be used in two cases. First off all, when their only purpose is to hold simple data (with no directly-related code) that's specific to a translation unit (a.k.a `.cpp` file). The second use comes from whenever you get into template metaprogramming, where they're simply an invaluable gem in terms of design. **CONTINUED**. – 3442 Sep 27 '15 at 06:11
  • 1
    @bvraghav: **CONTINUED**. The fact is that (most of the time), when used in template metaprogramming, they hold no data at all, neither related code because there's no related data. They usually hold stuff such `typedef`s or `constexpr size_t`s. In this case, the use of `class`es is simply stupid, because there's nothing to encapsulate at all. Please comment here if you have any other doubts related to the link :). – 3442 Sep 27 '15 at 06:14
  • Got the point---both of them. However, I am curious, why do you call a `.cpp` file as a _translation unit_? – hell_ical_vortex Sep 28 '15 at 07:14
  • 1
    @bvraghav: That's the name the standards use to refer to them. In a standardise point of view, you cannot assert that source files are *actually* stored as files, so the standards coined the abstract term *translation unit* instead for this purpose. See [this](http://stackoverflow.com/questions/1106149/what-is-a-translation-unit-in-c) for more information. – 3442 Sep 28 '15 at 22:25