Author: Gerd Isenberg
Date: 11:21:28 01/06/06
Go up one level in this thread
On January 06, 2006 at 13:08:20, Alexander Kure wrote: >On January 06, 2006 at 11:54:27, Gerd Isenberg wrote: > >>>>But doesn't hiding the implementation for this scalar wrapper class - without >>>>any virtuals - also imply that it don't cares whether a public interface is used >>>>internally but also privat members? >>> >>>Sorry Gerd, I don't quite understand your question. >>>Can you try to rephrase it? >>> >>>Best, >>>Alex >> >>If isAdjacent is a const member or friend, it has read access to >>private/proteceted members - but is not forced to do so - it may also use the >>public interface only. If so, should that be a reason to make the function not >>member or friend? > >This depends. If there is a performance issue that can be solved by having >access to the internal data representation of a class you should make this >function a member of that class. >Compare this to map member function std::map::find() and the algorithm >std::find(). The first one has O(log(n)) logarithmic time by making use of the >internal binary tree representation in the map class while the algorithm takes >O(n) linear time for lookup. >On the other side if you can implement a function by using a classes public >interface only then make this function a non-member function. Thus you also >decrease the dependency of clients to the public interface of that class. >Clients don't have to recompile because you want to add a new convenient >function to your class. Encapsulation and decoupling! I see. > >>Making a function a member of a class or not - should imho not only depend on >>its access rights, but also on the semantic and logical relations of the >>function. > >No. It should depend on encapsulation and decoupling issues. Ok. > >>I mean with some point and rectangle classes - i found it so far somehow natural >>or at least familiar to have the boolean isPointInRect as member of a rectangle. > >Good point. This function should not be part of the interface of the rectangle >class. IsPointInRect() is just a different concept. Making it part of the class >increases coupling of different concepts in one class. Seems i am biased by using mfc too much ;-) > >Another point: >Consider the standard C++ complex class. >Only operator+=() is defined as member function, but operator+() is not. >Why is this so? Encapsulation! You can implement operator+() outside the complex >class by just using complex' member function operator+=() Aha, very interesting. I had following in mind: Combined assignment operators or members like increment/decrement modify *this, which is already an existing object, refered as r- and l-value. So it is very "natural" to make those functions members of the class. Returning *this, thus a reference of this object: T& operator+=(const T &op). Binary operators like T friend operator+(const T &op1, const T &op2) don't have an existing object yet, but have to create one on the fly for the result. > >complex operator(const complex& lhs, const complex& rhs) >{ > complex temp = lhs; > temp += rhs; // calls temp.operator+=(rhs); > return(temp); >} Ok, i begin to understand. In my xmm-wrapper i still have disjoint implementations of eg. operator&= and operator&. It would be fine to have all binary operators in the base class, using the combined assignment operators of the derived classes. But i can not make pure virtual operator&= here, because of register incarnations of objects!? class DBB { friend class XMM; friend class GPR; public: DBB(){} DBB(const BitBoard &l, const BitBoard &h) {s[0] = l; s[1] = h;} ... protected: union { __m128i x; // this intrinsice type is wrapped here BitBoard s[2]; }; }; class XMM : public DBB { public: XMM(){} XMM(const DBB& a){x = a.x;} XMM(__m128i a){x = a;} ... XMM& operator&=(const XMM &a) { x = _mm_and_si128(x, a.x); return *this; } friend XMM operator& (const XMM &a, const XMM &b) { return XMM(_mm_and_si128(a.x, b.x)); } ... }; class GPR : public DBB { public: GPR(){} GPR(const BitBoard &l, const BitBoard &h) {s[0] = l; s[1] = h;} GPR(const DBB& a) {s[0] = a.s[0]; s[1] = a.s[1];} ... GPR& operator&=(const GPR &a) { s[0]&=a.s[0]; s[1]&=a.s[1]; return *this; } friend GPR operator&(const GPR &a, const GPR &b) { return GPR(a.s[0]&b.s[0], a.s[1]&b.s[1]); } ... }; .... // possible application: // pass either MMX or GPR as template type template <class T> void leAttacks(sTarget* pTarget, const sSource* pSource) { T gl(pSource->rooks); T pl(pSource->occup); pl = (~pl).notH(); gl |= pl & (gl>>1); pl &= pl>>1; gl |= pl & (gl>>2); pl &= pl>>2; gl |= pl & (gl>>4); (gl>>1).notH().store(&pTarget->le); } ... void rookAttacks(sTarget* pTarget, const sSource* pSource) { riAttacks<XMM>(pTarget, pSource); leAttacks<GPR>(pTarget, pSource); upAttacks<XMM>(pTarget, pSource); dnAttacks<GPR>(pTarget, pSource); } > >>May be my autodidactical oo-knowledge needs some lifting ;-) > >I recommend reading Scott Meyers' excellent book "Effectiv C++", Addison Wesley, >3rd edition. Thanks, Gerd
This page took 0 seconds to execute
Last modified: Thu, 15 Apr 21 08:11:13 -0700
Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.