I just learned about returning a reference... and it seems to me that I can delete my mutators now? ie.

class foo
{
int A;

//mutator
void set_A(const int a) {A=a;}

//accessor
int get_A() {return A;}

}

int main()
{
foo MyFoo;
int b = MyFoo.get_A(); //get
MyFoo.set_A(4); //set
}

vs

class foo
{
int A;

//accessor AND mutator
int& a() {return A};
}

int main()
{
 foo MyFoo;
int b= MyFoo.a(); //get
MyFoo.a() = 4; //set
}

Can anyone tell me when I should keep them separate and when I can combine them like this? Or do these do something different (ie. I am understanding it incorrectly) ?

Thanks!

Dave

Recommended Answers

All 14 Replies

and actually, why not just

class foo
{
int A;
};

int main()
{
foo MyFoo;
int b = MyFoo.A; //get
MyFoo.A = 4; //set
}

??

Well...that last example won't work because classes default to private. If you put a public: in front of the int A it would work.

However both of the last examples (directly accessing the member or a reference to the member) are violating the principle of data hiding. Anyone using the class now has to know about how it is implemented. (Granted that for this example there isn't much to know.)

What if in the future, the int value needed to be stored and retrieved from somewhere other than application memory? (Say a database or from another application) The last two examples would require client code to change. But the first example with the accessors (get and set) the clients wouldn't know (or care) where the int came from or went to, the implementation details of where the int is stored belong to the class.

Ok - but for simple cases (just like this one, where all it is doing is storing a number), it's not bad practice to just make it public?

Often times I just have a huge list of properties:

int NumPoints;
double ThetaMin, ThetaMax, PhiMin, PhiMax, PhiStep, ThetaStep;

And the list goes on and on.... then I have to write accessors and mutators for allllll of those, and the header becomes very cluttered.

Know what I mean?

Dave

>but for simple cases (just like this one, where all it is doing is
>storing a number), it's not bad practice to just make it public?
It depends on whom you ask this question. Some people will tell you that public data is absolutely evil. Others will tell you that it's okay sometimes, but you should avoid it.

In the usual case, accessors and mutators break encapsulation anyway because 99% of them consist of a member function wrapper around an unchecked access of the data member:

T KewlClass::GetX() const
{
  return x;
}

void KewlClass::SetX ( const T& value )
{
  x = value;
}

This clearly defeats the purpose of data hiding and the only way it's better than public data is future-proofing. You can decide to later change the implementation so that more checks are performed or something completely different happens, all without altering the interface.

Personally, I think that an excessive number of accessors and mutators is indicative of a design flaw. You should be focused more on the object itself, and how the object behaves as a whole, not the individual members of a class. This is why good OO design is so hard; a good interface is not nearly as simple as the tedium of a slew of accessors and mutators.

This is heading into what I think the key difference is in a lot of designs.

Are you using C++ and classes and polymorphism or are you writing object oriented programs. There is a distinct difference between a program with classes and a program written around objects.

The difficulty comes in coming up with the right object and the right interfaces to make it all work together.

I guess I don't know the difference between using class and writing object oriented programs? I thought an object oriented program was one which used classes (aka objects)?

What I have is a Scanner class and a Scan class. I do something like this

Scanner MyScanner;
Scan MyScan = MyScanner.TakeScan();

Is that enough to tell you which kind of programming this is?

Dave

>I thought an object oriented program was one which used classes (aka objects)?
I don't think anyone agrees on terminology, but I use classes regularly (they're awesome!) and I wouldn't say I write object oriented programs. I'd break it up like this:

Object Assisted: Basically a structured approach with the odd object here and there to help clean up dangling uglies. Beginner C++ follows this strategy, where you use the std::string class and such to make the otherwise C-like code "better".

Object Based: Regular use of classes, but no explicit inheritance. You write classes, but they're nearly always concrete and not designed for inheritance. Any use of inheritance is brief and minimal.

Object Oriented: Full inheritance hierarchies, polymorphism, and other big words. This is the super scary (and often impenetrable) stuff that people like Uncle Bob write about.

>Is that enough to tell you which kind of programming this is?
I'd call it object based.

First of all, Narue's definitions are BS. (What the fuck is a "full" inheritance hierarchy?)

Second, mutators are almost always a sign of design problems, unless your problem provably requires mutators, but getters aren't. Well, if you have mutators that aren't provably necessary, it's evidence that there's a "better" design (one which involves completely immutable objects, for some definition of the term), but it doesn't mean yours is terrible. Of course, considering the low-level nature of C++, I'd say this paragraph really applies more to high level languages like C# or low-level languages like Java.

Third, it's perfectly fine to return a reference and modify it, instead of having methods named 'setFoo' -- if you ever need to change your mind about that design decision, you can change the interface later and then get a bunch of compiler errors, and update the code that corresponds to each compiler error accordingly. On the other hand, using explicit functions like getFoo and setFoo might require less work for small changes.

I don't understand how you would not need mutators...

class Scanner
{
Point Location;

Scanner(Point &Loc) : Location(Loc) {}
};

int main()
{
Scanner MyScanner(Point(0,1,0));

//now how do I move the scanner to a new location without a mutator setLocation() ?

return 0;
}

What do you think of that example?

Dave

I would construct a new Scanner with a different location.

but the constructor really looks like

Scanner::Scanner(int NumPoints, int MinTheta, int MaxTheta, int ThetaStep, int MinPhi, int MaxPhi, int PhiStep, Point Location, Transformation T, double PointSpeed)

It seems absolutely crazy to me to construct a new Scanner when all that needs to be changed is the location, no?

First of all, it would be, at the very least, Scanner::Scanner(int NumPoints, RangeStep& Theta, RangeStep& Phi, Point Location, Transformation T, double PointSpeed) , or even something more clumpy. I suspect I'd only have 2 or 3 parameters to the constructor, the rest part of helper classes.

Second, I'd probably pass some kind of reference-like type to the object that contains most of those parameters -- the ones that don't get copied over and over again.

Third, if there are serious performance worries, I would modify it in place. This isn't a religion I'm talking about, it's practical software engineering. If you're talking about low level numerical algorithms or stuff like simple loops that would require tail recursion to do without modifying anything, I would have no problem overwriting variables -- it makes sense then and it's worth it. There's virtually nothing wrong with modifying variables in place when the mutation is never seen outside a single function.

But if you're talking about some situation like "I have an airplane schedule" or "I have a text editor buffer data structure", those things are going to be immutable. Allowing mutable variables imposes limitations on what your design can be.

It's all based on your object model. If you write the model so that you need to set the location, then you need a setLocation.

I'm sure I could come up with (or you could come up with) several examples that require either public access or mutators. For example, your Point class (or struct) very likely has public or mutable members.

In this scanner case (and I don't have any idea what real-word element this represents if any, so take these with a grain of salt) I have a few questions:

Does the scanner NEED to know where it is, or should the position of the scanner be wrapped in a higher-level object?

If the scanner needs the location, how is the location used?

Might you update the scanner position with move() and/or moveTo() instead of setLocation()? (that would presume that the scanner has input or control of where it is)

In the end its all about how you model the object (or objects) in your system, a lot of which can be very personal and involve strong opinions.

I know on one of my larger projects we had fairly in-depth (and sometimes somewhat heated) discussions about what objects should exist, what they should be named, what their job should be, what they needed to know to get their job done and how they would talk to the other objects in the system. People had real opinions about how the system should fit together and we didn't always agree right away. Sometime the choice became obvious quickly, sometimes we had to step back and look at the problem again attempting to consider or accommodate the other viewpoint more to come to an agreement.

Thanks for the discussion guys - gives me something to think about.

First of all, it would be, at the very least, Scanner::Scanner(int NumPoints, RangeStep& Theta, RangeStep& Phi, Point Location, Transformation T, double PointSpeed) , or even something more clumpy. I suspect I'd only have 2 or 3 parameters to the constructor, the rest part of helper classes.

What do you mean "helper classes"? Can you give a brief example of how to break that constructor into

Scanner::Scanner(Transformation T)

and a helper class?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.