Saturday, May 31, 2008

114. IsA or HasA?When designing a class hierarchy, you may face a decision between inheritance (aka IsA ) vs. containment (aka HasA) relation. For instance, if you are designing a Radio class, and you already have the following classes implemented for you in some library: Dial, ElectricAppliance. It is quite obvious that your Radio should be derived from ElectricAppliance. However, it is not so obvious that Radioshould also be derived from Dial. How to decide? You can check whether there is always a 1:1 relation between the two, e.g., do all radios have one and only one dial? You may realize that the answer is "no": a radio can have no dial at all (a transmitter/receiver adjusted to a fixed frequency) or may have more than one (both an FM dial and AM dial). Hence, your Radioclass should be designed to have Dial(s) instead of being derived from it. Note that the relation between Radio and ElectricAppliance is always 1:1 and corroborates the decision to derive Radio from ElectricAppliance115. Static member functionA static member function in a class can access only other static members of a class or variables which are not members of its class. It can be invoked even without an object instance, unlike any other member functions:

stat::print(); //no object instance requiredstat s(1);s.print(); //but still can be called from an object

}//end mainStatic members are used when all other data members of an object are also static; when the function does not depend on any other object member (like print() above); or simply when a global function is undesirable so it is wrapped in a class.116. Calling an object's member function from its constructorAn object's member functions (both virtual and non-virtual) can be called from its constructor. The invoked virtual is guaranteed to be the one defined in the current object (or higher, if it has not been overridden in the current object). However, virtuals of objects derived from the one whose constructor is being executed are not called.

}The compiler re-edits the above sample as if the programmer had written:

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //"c=5;" transformed by the compiler into something like this: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

C temp(5);//temporary object instantiated, c = temp; //assigned using operator = temp.C::~C(); //temp's destructor activatedIn many cases, this conversion is intentional and well-behaved. But there are cases where such automatic conversion is undesirable, like the following:

class secret { friend class spy;//spy can access all members of 'secret' friend void encrypt(string & rep);//...and so can encrypt private: string report; public: void scramble() { ::encrypt(report); } void transmit() const { spy::transmit(report); }};Notes about friendship: 1. A friend declaration exposes implementations details of its class, so it should be used wisely. However, friendship has the advantage of allowing code re-use in a simple manner; in fact, many of the standard functions and overloaded operators are used in standard containers (like string<>) by means of friendship. 2. Friendship is not inherited, so non-public members of any class derived from secret are not accessible to spy and encrypt. 119. A static class memberA class member declared static is a single instance of that member shared by all instances of this class (that's why it is sometimes termed a class variable, as opposed to object variable). This feature has many uses: for instance, in order to create a file lock, one can use a static bool class member. An object trying to access this file has to check first whether the static (i.e., shared) flag is false. If it is, the object turns the flag on and processes the file safely, since other objects will now find that the flag is now on, and hence -- they cannot access the file. When the object processing the file is done, it has to turn off the flag, enabling another object to access it. How is a static member created?

bool fileProc::isLocked; //definition; initialized to 'false' by default. Note: no 'static' here120. Prefer dynamic_cast<> to typeid()A robust, long lasting OO design relies on the premise that an existing class can be re-used in the future by means of derivation. Therefore, examining the actual type of an object with typeid() results in a code which is less flexible, since it cannot handle derived objects:

void Registry::Register (const Window& wind) //has to receive a Window object exclusively{if (typeid(wind) == typeid(Window)) //inflexible; objects derived from Window will fail this test{Store ( wind.GetHandle() );} else //object derived from Window was received; not handled { cout<< "Window object expected!"<<endl;} }The use of dynamic_cast <> rather than typeid() is a better choice - it will enable the Registry::Register() member function to cope with a Window object as well as any object derived from it:

void Registry::Register (const Window& wind) //has to receive a Window object exclusively{Window w=dynamic_cast<Window&>> (wind) //will succeed even with derived objects{Store ( w.GetHandle() ); //it is guaranteed that Window::GetHandle() is called }}121. The Type of a Class TemplateA template name is not a type. When you need a template functioning as a full type (for example: to serve as a base class or as a function argument), you have to specify its arguments:

size_t n = sizeof(vector); //compile time error; template argument list is requiredsize_t n = sizeof(vector<int>); //fineclass myCharVector : public vector<char> { //also fine//...};122. Standard vs. User Defined Conversions in Overloaded Function CallA non-explicit constructor taking a single argument is also a conversion operator, which casts its argument to an object of the constructor's class. When the compiler has to resolve an overloaded function call, it takes into consideration this user-defined cast:

f(5f); //calls f(double); standard conversions have higher precedence than user-defined onesThis is because float variables are converted to double automatically in order to match an overloaded function signature. This type promotion is defined by the C++ standard. On the other hand, the conversion of float to Numeric is user-defined. User defined conversions rank lower than standard ones when the compiler has to decide which overloaded version is to be called.123. Be Cautious with Local Static Variables in a Member FunctionThe object model of C++ ensures that each object instance gets its own copy of data members (except for static ones). On the other hand, all instances of a base class, as well as instances of classes derived from it, share a single copy of the member functions of the base class. Therefore, declaring a local static variable (not to be confused with static class members) in a member function can cause surprises like this:

class Base { public: int countCalls() { static int cnt = 0; return ++cnt; } };class Derived1 : public Base { /*..*/};class Derived2 : public Base { /*..*/};Derived1 d1;int d1Calls = d1.countCalls(); //d1Calls = 1Derived2 d2;int d2Calls = d2.countCalls(); //d2Calls = 2 and not 1 The above example may be used to measure load balancing by counting the total number of invocations of the countCalls member function, regardless of the actual object from which it was called. However, obviously that programmer's intention was to count the number of invocations through Derived2 class exclusively. In order to achieve that, a static class member would be preferable:

No comments:

About Me

Hi,
I work in Hyderabad India. As a software Engineer I am the common man of India. A little sarcastic and cynic as every Indian child grows up to be. I have started to blog again after a long gap of two years. ( As If people are waiting ) . Will try my best to keep this informative since collection of knowledge is all that I know