Reputation: 387
Out of curiosity, I would like to know if the following class:
class Number
{
public:
double value;
double square;
};
can be changed (using custom types, proxy, ...) such as a change of Number::value would trigger the computation of Number::square, without using accessors (like Number::setValue())
I think a custom type instead of double could overload operator=, thus intercepting the assignment for a value (by the way is this the Proxy pattern?). But then I would need a way to notify the instance of the class Number that something has changed.
I'm confident that C++ is powerful enough to allow this, but maybe I'm just wrong.
Upvotes: 2
Views: 449
Reputation: 145359
Intercepting assignment to a member involves, as you speculate, to use a type with user defined assignment operator.
I have used that technique for debugging spaghetti systems. The idea is then to remove the interception once it's clear where the assignments are coming from, what order things happen in, or whatever information one is after. For it has some severe costs:
Call overhead in debug builds.
Not perfect conversion back to original type, i.e. not automatically dealing with all usage scenarios.
Precludes user code obtaining reference or pointer to the item.
In order to have assignment automatically update some other item, you need a pointer or reference to that item or containing structure. This introduces further space and execution overhead. Thus, except for the debugging scenario it's probably best to use an ordinary setter function, rather than implicit assignment interception: in general, explicit is good, implicit is bad.
Addendum: I forgot to remark on the particular usage scenario in the question, which may well be the “real” issue?
Anyway, when one value B is determined by a value A, the usual solution is to provide a function to obtain B, instead of storing the B value. If B is stored, then it should not be accessible to user code that can modify it, because that can easily attract incorrect modifications. So it should then be protected
or private
(preferably the latter), and using code should be offered an accessor function.
The only good reason for storing it, however, is when it's costly to compute, and in this case one wants to defer to computation until the value is requested, a "lazy" computation. So then, if the value itself cannot signal whether it's been updated, some boolean or other means to check if the value is up to date is needed. Finally, the update might be needed on a const
object, and hence the stored value should be declared mutable
.
Summing up, when the storage and update of B as a data item is for efficiency, with the B item as just a cache of that value, then
B should be non-public
and mutable
,
there should be a boolean or other means to determine if an update is needed, and
there should be a public accessor function.
These issues are quite different from the issues of implicit assignment interception. ;-)
Upvotes: 3
Reputation: 279315
Your proxy idea can certainly work:
class Number;
struct Proxy {
Proxy(Number *parent) : parent(parent) {}
Proxy &operator=(double d);
operator double() { return value; }
private:
Number *parent;
double value;
};
class Number
{
public:
Proxy value;
double square;
Number() : value(this) {}
// I imagine you want a double ctor too
};
Proxy &Proxy::operator=(double d) {
value = d;
parent->square = d * d;
return *this;
}
You still have a potential problem of someone assigning to square
. You can add another proxy, but the hassle of all this is why it is idiomatic in C++ to use accessor functions rather than direct property access for classes that enforce invariants.
Upvotes: 2
Reputation: 2216
What you are doing sounds like an instance of the 'observer' or 'listener' pattern in C++
you just need a code hook from which to call 'notifyObservers' - which as you said could be in an overloaded operator= (it would naturally fit in a setter method but you prefer not to have these)
Upvotes: 2