Quote:Original post by SneftelQuote:Original post by phresnel
Fear not, I don't do so (and actually never have done). I was answering on Telastyns example
That still doesn't answer my question. The code you posted doesn't allow property getter/setters to access the encompassing class, making them essentially useless as far as doing anything you'd actually want to do with properties (other than print a debugging message, I suppose).
I see. In that case, you can still cherry pick what you pipe into the properties, let it be single primitive types or the whole parent class, let them be mutable or const (see class Foo::Delta):
struct Foo { // Properties using unnamed classes. class Alpha { int value; public: int & operator = (const int &i) { return value = i; } operator int () const { return value; } } alpha; // Cherry picking. class Bravo { float value; Alpha & alpha; public: float & operator = (const float &f) { alpha = 0xdeadbeef; return value = f; } operator float () const { return value; } Bravo (Alpha α) : alpha (alpha) {}; } bravo; // Alias for bravo. No cherry picking this time. class Charlie { Foo & foo; public: float & operator = (const float &f) { return foo.bravo = f; } operator float () const { return foo.bravo; } Charlie (Foo &foo) : foo (foo) {}; } charlie; // Readonly alias for bravo. No cherry picking this time. /* class Delta { const Foo & foo; public: float & operator = (const float &f) { return foo.bravo = f; } // < compile time error, as foo is immutable operator float () const { return foo.bravo; } Delta (const Foo &foo) : foo (foo) {}; } delta; */ Foo () : bravo (alpha), charlie (*this), delta (*this) {}};int main () { Foo foo; foo.charlie = 5.132f; ::std::cout << ::std::hex << foo.alpha << ", " << foo.bravo << ", " << ::std::endl;}
Allowedly, Foo will grow and grow in bytesize. So as for the size, clearly an advantage for c#. But as for the degree of freedom w.r.t. security levels, I'd say advantage for c++. But all in all, I am unsure which properties and property-hacks are better. C++ is good if you are paranoid, and make everything const- and restricted-first. But c#'s syntax is clearly the easier and more compact, plus it doesn't need additional storage.
To give a more realistic example:
#include <iostream>class Angle { float value; class Radian { float &value; public: float & operator = (const float &f) { return value = f; } operator float () const { return value; } Radian (float &value) : value (value) {} }; class Degree { float &value; public: float & operator = (const float &f) { return value = f * 0.0174532925f; } operator float () const { return value * 57.2957796f; } Degree (float &value) : value (value) {} };public: Radian radian; Degree degree; Angle () : radian (value), degree (value) {}};int main () { Angle alpha; alpha.degree = 180.0f; ::std::cout << alpha.degree << " degrees = " << alpha.radian << " radians\n"; alpha.radian = 3.1415927f * 0.5f; ::std::cout << sizeof (alpha) << "]" << alpha.degree << " degrees = " << alpha.radian << " radians\n";}
or just (main function skipped)
class Angle { class Degree { float &value; public: float & operator = (const float &f) { return value = f * 0.0174532925f; } operator float () const { return value * 57.2957796f; } Degree (float &value) : value (value) {} };public: float radian; Degree degree; Angle () : degree (radian) {}};
compared to
class Angle { float value; public float Radian { get { return this.value; } set { this.value = value; } } public float Degree { get { return this.value * 57.2957796f; } set { this.value = value * 0.0174532925f; } }};namespace ConsoleApplication1 { class Program { static void Main (string [] args) { Angle alpha = new Angle (); alpha.Degree = 180.0f; System.Console.WriteLine ("{0} degrees = {1} radians", alpha.Degree, alpha.Radian); alpha.Radian = 3.1415927f * 0.5f; System.Console.WriteLine ("{0} degrees = {1} radians", alpha.Degree, alpha.Radian); } }}
or just (main function skipped)
class Angle { public float Radian; public float Degree { get { return Radian * 57.2957796f; } set { Radian = value * 0.0174532925f; } }}
.
Quote:Original post by mikeman
Sure, writing get/set for each and every one of your private data is sign of bad design, but some properties are meant to be public. Like the color of a Renderable. You could name it SetColor(),ReColor(),RePaint() or what have you, but in essense it's a setter. I think there's a line between encapsulating and making your objects xenophobic.
There the divergence already begins, as in my world the things I render can have a different color at each spacetime-point on it's surface (yes, it's about a ray tracer), making it impossible to supply a virtual SetColor method for all of them (just imagine spheres, cubes, player-models, or whole terrain including flora).
edit: But one thing I dislike about properties in general: They look like peas when used, but can really be coconuts. I like the convention where you name your functions like convertToYYY() or computeZZZ(), when some work is done internally, so fellow programmers don't underestimate a line of code in performance when in eye parse mode. And for the same reason, I virtually never use get/set-calls, because it makes sense only in the rarest conditions (that is, in my code).
Also, I really wouldn't use the above Degree/Radian-Angle class, as it is hard to analyze which form is used more often in general (and based on that knowledge to decide which form is first class), but the wrong decision would introduce a plethora of unneccessary multiplications. So I would instead provide two distinct classes (again using the radian/degree case, but you could also apply this to other things, e.g. normal vectors / general vectors / points):
// NOTE: if you detect a "°" somewhere in the following,// please replace it with an actual "&" and a following "deg"namespace { const float to_degree = 57.2957796f; const float to_radian = 0.0174532925f;}class Degree;class Radian { float value; public: Radian (const float &init) : value (init) {} explicit inline Radian (const Degree &); inline operator Degree () const ; inline Radian & operator = (const Degree &rad); operator float () const { return value; } float & operator = (const float &f) { return value = f; }};class Degree { float value; public: Degree (const float &init) : value (init) {} explicit inline Degree (const Radian &rad) ; inline operator Radian () const ; inline Degree & operator = (const Radian &rad); operator float () const { return value; } float & operator = (const float &f) { return value = f; }};inline Radian::Radian (const Degree & deg) : value (to_radian * deg) {}inline Radian & Radian::operator = (const Degree & deg) { value = to_radian * deg; return *this; }inline Radian::operator Degree () const { return Degree (value * to_degree); }inline Degree::Degree (const Radian &rad) : value (to_degree * rad) {}inline Degree & Degree::operator = (const Radian &rad) { value = to_degree * rad; return *this; }inline Degree::operator Radian () const { return Radian (value * to_radian); }
This erases hidden computations, and introduces additional ones only on demand:
int main () { Degree deg = 90.0f; Radian radA (deg); Radian radB = deg; ::std::cout << deg << " degrees = " << radA << " radians = " << radA << " radians\n"; Degree degB = 45.0f + deg + 45.0f + (Degree)radA; ::std::cout << "degB = " << degB << "\n";}
[Edited by - phresnel on January 26, 2009 8:23:40 AM]