[ACCEPTED]-Getter and setter, pointers or references, and good syntax to use in c++?-getter

Accepted answer
Score: 17

As a general law:

  • If NULL is a valid parameter or return value, use pointers.
  • If NULL is NOT a valid parameter or return value, use references.

So if the setter should 8 possibly be called with NULL, use a pointer 7 as a parameter. Otherwise use a reference.

If 6 it's valid to call the getter of a object 5 containing a NULL pointer, it should return 4 a pointer. If such a case is an illegal 3 invariant, the return value should be a 2 reference. The getter then should throw 1 a exception, if the member variable is NULL.

Score: 7

Your code looks a great deal as if you're 49 accustomed to a different language -- in 48 C++ using this->x (for one example) is relatively 47 unusual. When the code is at all well written, so 46 is using an accessor or mutator.

Though I'm 45 fairly unusual in this particular respect, I'll 44 go on record (yet again) as saying that 43 forcing client code to use an accessor or 42 mutator directly is a bad idea. If you honestly 41 have a situation where it makes sense for 40 client code to manipulate a value in your 39 object, then the client code should use 38 normal assignment to read and/or write that 37 value.

When/if you need to control what value 36 is assigned, operator overloading lets you 35 take that control without forcing ugly get/set 34 syntax on the client code. Specifically, what 33 you want is a proxy class (or class template). Just 32 for one example, one of the most common 31 situations where people want get/set functions 30 is something like a number that's supposed 29 to be restricted to some particular range. The 28 setXXX checks the new value for being in range, and 27 the getXXX returns the value.

If you want that, a 26 (fairly) simple template can do the job 25 much more cleanly:

template <class T, class less=std::less<T> >
class bounded {
    const T lower_, upper_;
    T val_;

    bool check(T const &value) {
        return less()(value, lower_) || less()(upper_, value);
    }

    void assign(T const &value) {
        if (check(value))
            throw std::domain_error("Out of Range");
        val_ = value;
    }

public:
    bounded(T const &lower, T const &upper) 
        : lower_(lower), upper_(upper) {}

    bounded(bounded const &init) 
        : lower_(init.lower), upper_(init.upper)
    { 
        assign(init); 
    }

    bounded &operator=(T const &v) { assign(v);  return *this; }

    operator T() const { return val_; }

    friend std::istream &operator>>(std::istream &is, bounded &b) {
        T temp;
        is >> temp;

        if (b.check(temp))
            is.setstate(std::ios::failbit);
        else
            b.val_ = temp;
        return is;
    }
};

This also makes the code 24 much closer to self documenting -- for example, when 23 you declare an object like: bounded<int>(1, 1024);, it's immediately 22 apparent that the intent is an integer in 21 the range of 1 to 1024. The only part somebody 20 might find open to question is whether 1 and/or 19 1024 is included in the range. This is considerably 18 different from defining an int in the class, and 17 expecting everybody who ever looks at the 16 class to realize that they're supposed to 15 use the setXXX to enforce some (at that 14 point unknown) set of bounds on the values 13 that can be assigned.

When you embed one 12 of these in a class, you make it a public 11 variable, and the range is still enforced. In 10 the client code, there's no real argument 9 over syntax -- you're just assigning to 8 a public variable, like you would any other 7 -- with the minor detail that attempting 6 to assign a value that's out of range will 5 throw an exception. In theory, the class 4 should probably take a policy template-parameter 3 to specify exactly what it does in that 2 case, but I've never had a real reason to 1 bother with that.

Score: 6

The best thing is to provide a real OO interface 2 to the client that hides implementaton details. Getters 1 and Setters are not OO.

Score: 4

As others have said, use pointers if null 17 is a possibility.

In most cases, I prefer 16 to use references when possible. Personally, in 15 my code, I like to use the distinction between 14 pointers and references to signal ownership. I 13 think of calls with references as "loaning" an 12 object to another function or class. The 11 original class that passed or returned the 10 reference still owns it, and is responsible 9 for its creation, maintenance and clean 8 up. When my code passes a non-const pointer, on 7 the other hand, it usually means that there's 6 some kind of transfer or sharing of ownership 5 going on, with all the responsibilities 4 that entails.

(And yes, I usually use smart 3 pointers. Those are akin to references 2 in my mind. I'm talking about lower level 1 code than that here.)

Score: 2

whats the difference between them?

The reference 19 is an alias of the thing(it is the thing*). A 18 pointer is the address of the thing. If 17 there's a chance that what's pointed to 16 won't be there, then you probably don't 15 want to return references. References tell 14 the caller "I'm going to give you an alias 13 that will exist when I return it to you". In 12 fact there's really no way to check the 11 reference to see if what's underlying is 10 valid.

With the pointer, semantically, you 9 are implying that the caller may wish to 8 check to see if Member exists before using 7 it. Ussually this is done with a NULL check.

Ultimately 6 there's no "right" answer. It depends on 5 the class's contract and if the caller will/should/wants 4 to check whether "Member" is still around.

The 3 short answer is pointers for things that 2 can be pointed elsewhere and references 1 for "unseated" aliases.

Score: 2

In addition to the other answers, if you 8 choose references for the getter don't write 7 it like in your example:

YourClass &Member(){
   return *this->pMember;
}

Your getter actually 6 allows setting, as in instance->Member() = YourClass(); and thus bypassing 5 your setter. This might not be allowed if 4 YourClass is noncopyable, but is still another 3 thing to have in mind. Another drawback 2 is the getter is not const.

Instead, write 1 your getter like this:

const YourClass &Member() const {
   return *this->pMember;
}
Score: 1

+1 on questioning the use of setters and 3 getters. If you must use them and have the 2 possibility of nulls consider using boost::shared_ptr. This 1 way ownership is handled for you.

Score: 0

Jonathan, what compiler are you using? There's 2 a great chance that shared_ptr already comes shipped 1 with it as part of the compiler's TR1 implementation.

More Related questions