Saturday, May 31, 2008

C++ #22

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
Radio
should 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
Radio
class 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
ElectricAppliance
115. Static member function
A 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:

class stat{
int num;
public:
stat(int n = 0) {num=n;}
static void print() {cout <<"static member function" <<endl;
};

void main() {

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

}//end main
Static 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 constructor
An 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.

class A {
public:
virtual void f() {}
virtual void g() {}
};
class B: public A {
public:
void f () {} //overriding A::f()
B() { f(); //calls B::f()
g(); //g() not overriden in B, therefore calling A::g() }
};
Mind that if the object's member functions use object data members, it is the implementor's responsibility to initialize them first, preferably by a mem-initializer list:

class C {
int n;
int getn() const { cout<<n<<endl; }
public:
C(int j) : n(j) { getn(); } //Fine: n initialized before getn()
//is called; otherwise - n would
//have an undefined value
};
117. The Explicit Keyword
A constructor taking a single argument is by default an implicit conversion operator:

class C {
int I;
//...
public:
C(int i);//constructor and implicit conversion operator
//as well
};

void f() {

C c(0);

c = 5; //implicit conversion of 5 to a C object and
//then assignment

}
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 activated
In many cases, this conversion is intentional and well-behaved. But there are cases where such automatic conversion is undesirable, like the following:

class String {
int size;
char *p;
//..
public:
String (int sz); //constructor & implicit conversion
//operator; undesirable in this case
};
void f ()
{
String s(10);

//the following is a programmer's typo; yet it is not
//detected since it has an unexpected interpretation:

s = 100; //bad; 100 is converted to a String and then
//assigned to s.
}
In order to avoid such implicit conversions, a constructor taking one argument should be declared as explicit:

class String {
int size;
char *p;
//..
public:
//no implicit conversion
explicit String (int sz); //no implicit conversion
String (const char *s, int size n = 0); //implicit conv.
};

void f ()
{
String s(10);

s = 100; //now compile time error; explicit conversion
//required now:

s = String(100); //fine; explicit conversion
s = "st";//fine; implicit conversion allowed in this case

}
118. That's what friends are for
A class can declare external functions or other classes as friends. Friendship grants full access to all of the grantor's members, even private and protected ones:

void encrypt (string & rep) {/*..*/} //global function

class spy {
public:
static void transmit(const string& rep) { /*..*/}
//...
};

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 member
A 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?

class fileProc {
FILE *p;
static bool isLocked; //only a declaration; see definition below...
public:
bool isLocked () const {return isLocked; }
};

//somewhere outside the class definition:

bool fileProc::isLocked; //definition; initialized to 'false' by default. Note: no 'static' here
120. 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 Template
A 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 required
size_t n = sizeof(vector<int>); //fine
class myCharVector : public vector<char> { //also fine
//...
};
122. Standard vs. User Defined Conversions in Overloaded Function Call
A 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:

class Numeric {
float f;
public:
Numeric(float ff): f(ff) {}//constructor is also a float-to-Numeric conversion operator
};
void f(Numeric&); //function prototype
Numeric num(0.05); //object
f(5f); //calls void f(Numeric&). Numeric's constructor converts argument to a Numeric object
However, suppose we add a new overloaded version of f:

void f (double);
Now the same function call will resolve differently:

f(5f); //calls f(double); standard conversions have higher precedence than user-defined ones
This 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 Function
The 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 = 1
Derived2 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:

class Base {
private: static int i; //static class member rather than a local static variable
public: virtual int countCalls() { return ++i; } };
class Derived1 : public Base {
private: static int i; //hides Base::i
public: int countCalls() { return ++i; } //overrides Base:: countCalls() };
class Derived2 : public Base {
private: static int i; //hides Base::i and distinct from Derived1::i
public: virtual int countCalls() { return ++i; } };
Derived1 d1; Derived2 d2;
int d1Calls = d1.countCalls(); //d1Calls = 1
int d2Calls = d2.countCalls(); //d2Calls also = 1

C++ #21

101. Built-in types have a constructor
In standard C++ built-in types such as char, int and float can be initialized like any ordinary user-defined type:

char *pc = new char('a');
int pi= new int(10);
float pf = new float(0.333);
This language extension was introduced to the Standard in order to be compatible with STL containers and algorithms

102. Vector Elements are not Guaranteed to Occupy Contiguous Memory
Built-in arrays in C++ reside in contiguous chunks of memory. The Standard, however, does not guarantee that elements of a vector occupy contiguous memory. All current STL implementations store vector elements in contiguous memory, but it's just a convention, not a requirement.

103. STL Iterators are not Necessarily Pointers
Most implementations of STL (Standard Template Library) use pointers as the underlying representation of iterators. However, an iterator doesn't have to be a pointer, and there's a good reason for that. Consider a huge vector of scanned images stored on a 6-Gigabyte disk. A built-in pointer on most machines has only 32 bits, which wouldn't be large enough to iterate through such a large vector. Instead, the implementer can use a 64-bit integer as the underlying iterator in this case.

104. Const Iterators
You can think of an iterator as a pointer to elements of a container. All STL (Standard Template Library) containers provide the basic iterator type along with its const counterpart. The const iterator of a container should be used when the elements accessed through it are not supposed to be modified, like in the following example:

void main() {
string s ("hello world"); // s contains 11 characters
string::const_iterator p = s.begin(); //p is a const iterator pointing to the first element of s
size_t n = 0;

while ( p < s.end() ) { // a loop that count the number of chars in s
n++;
p++; // move to the next element of s
}
cout<<n;
}

105. The begin() and end() Member Functions of STL Containers

All STL (Standard Template Library) containers provide the begin()/end() pair of member functions. The member function begin() returns an iterator that points to the first element of the container:

vector < int > v(1); // create a vector of int with one element
v[0] = 10; // assign a value to the first element of v
vector<int>::const_iterator p = v.begin(); // p points to the first element of v
cout << *p; //output 10
The member function end(), however, returns an iterator pointing one position past the last valid element of the container. This may sound surprising at first, but there's nothing really unusual about it if you consider how strings in C are represented. An additional null character is automatically appended one position past the final element of the char array. The additional element in STL has a similar role; it denotes the end of the container, and is automatically appended to the container.

106. What's in an Allocator?
Every STL container uses an allocator that encapsulates the memory model that the specific platform uses. Allocators hide the platform-dependent details such as the size of pointers, memory organization, reallocation model, and memory page size. Since a container can work with different allocator types, it can easily work in different environments simply by plugging a different allocator to it. Every implementation provides a suitable allocator by default. Normally, users should not override the default allocator but they may do so for special purposes.

Calss
107. Const and Reference Data Members Must be Initialized
A class may have const and reference data members. These members must be explicitly initialized by a member-initialization list:

class Allocator
{
private:
const int chunk_size;
const string & serialization_path; // file into which an object can be serialized
public:
Allocator(int size, const string& path) : chunk_size(size), serialization_path (path){}
};

108. Construct an Object on a Pre-Allocated char Array
The memory buffer returned by operator new is guaranteed to have the suitable alignment properties for any type object. This property enables you to construct objects on such a pre-allocated char array. For example:

#include <new>
#include <iostream>
#include <string>
using namespace std;

class Employee{ /*...*/};

void f(){
char * pc = new char[sizeof(Employee)];
Employee *pemp = new (pc) Employee; //construct on properly aligned char array
//...use pemp
pemp->Employee::~Employee(); //explicit destruction
delete [] pc;
}

109. Prefer Initialization to Assignment of Objects
In general, assignment requires the creation and destruction of a temporary object, whereas initialization does not. This is true for not just for strings, but for every object type, so whenever you have the choice--prefer initialization to assignment. Consider this code snippet:

string s1 = "Hello ", s2 = "World", both;
both = s1+s2; // assignment rather than initialization; inefficient
The compiler transforms the expression both = s1+s2; into:

string temp (s1 + s2); // store result in a temporary
both = temp; // assign
temp.Date::~Date(); // destroy temp
This snippet could have been written in a much more efficient way:

string s1 = "Hello ", s2 = "World";
string both = s1+s2; // initialization is more efficient than assignment
This time, the compiler transforms the expression both = s1+s2; into:

string both (s1 + s2);
110. Destructors Should Handle Exceptions Locally
It is legal to call a function that may throw an exception inside a destructor. However, it is important to handle any exception that might be thrown from such a function inside the destructor body.

void cleanup() throw (int);

class C {
public:
~C();
};

C::~C() {
try {
cleanup();
}
catch(int) {
//handle the exception
}
}
The exception thrown by cleanup() is handled inside the destructor. Otherwise, the thrown exception will propagate outside the destructor, and if the destructor has been invoked while unwinding the stack due to another exception, terminate() will be called.

111. A String Literal Cannot be Used as a Template Argument
A template can take as an argument the address of an object with external linkage. A string literal cannot be used as a template argument since it has internal linkage. For example:

template <class T, const char *> class A
{/*…*/};
const char *global_ptr;
void array_user()
{
const char * p ="illegal";
A<int, "invalid"> aa; // error, string literal used as a template argument
A<int, p> ab; // also an error, p has internal linkage
A<int, global_ptr > ac; // OK
}
112. Defining Static Data Members of a Template
Templates can have static data members. A definition for a static data member can appear in a namespace scope enclosing the definition of the class template that contains the static member. For example:

template<class T> class C
{
public:
static T stat; //declaration
};
template<class T> T C<T>::stat = 5; //definition
A static data member can be accessed like this:

void f()
{
int n = C<int>::stat;
}
113. Use a Using-Declaration Instead of an Access Declaration
The access specifier of a base class member can be changed in a derived class by an access declaration, as in this example:

class A
{
public:
int n;
};

class D : public A
{
private:
A::n; // access declaration changes access of A::n to private; deprecated
};
According the ANSI/ISO Standard, the use of access declarations is considered deprecated. Instead, you should use a using declaration for that purpose:

class D : public A // using-declaration version
{
private:
using A::n; // using declaration changes the access of A::n to private
};

C++ #20

93. Why qsort is Still Useful in C++
C++ defines a set of generic algorithms such as sort and find. However, the corresponding C algorithms, qsort and bsearch, are still useful in C++ programs for at least three reasons:
• Legacy code. Familiarity with C algorithms is needed to maintain legacy C code.
• Efficiency. You cannot apply STL algorithms to items that are not stored in an STL container. To apply these algorithms to a built-in array, you first have to copy it into a container--an operation that incurs runtime overhead.
• Applicability to non-OO data types. STL algorithms rely on operators == and >. However, these operators are either meaningless or not defined when applied to plain structs or built-in arrays. C algorithms do not rely on these operators to work.

94. The Copy Algorithm

The Standard Library provides a generic copy function, which can be used to copy a sequence of objects to a specified target. The first and the second arguments of copy() are const iterators that mark the sequence's beginning and its end, respectively. The third argument points to a container into which the sequence is copied. The following example demonstrates how to copy the elements of a list into a vector:

#include <algorithm>
#include<list>
#include<vector>
using namespace std;

void main()
{
list<int> li; vector <int> vi;
li.push_back(1); // fill the list with elements
li.push_back(2);
vi.reserve( li.size() ); // a vector must make room for copied elements in advance
copy (li.begin(), li.end(), vi.begin() ); //copy list elements into vector, starting at vector's beginning
}

95. std::string Should Never be Initialized With a NULL Pointer
You can initialize std::string with a pointer to char. However, it does not check the pointer. When the pointer happens to be NULL the results are undefined:

#include<string>
using std::string;

const char * getDescription(int symbol); // returns NULL when symbol cannot not found

void writeToString (string & str, int symbol){
str = getDescription(symbol); // sloppy: initializer might be NULL; undefined behavior in this case
}
To be on the safe side, you should explicitly check the pointer before you assign it:

void writeToString ( string & str, int symbol){
const char *p = getDescription(symbol);
if (p) {
str = p; // now safe
}
}
96. Various Forms of Initializing std::string
Class std::string can be initialized in five forms: by a C-string, by part of a C-string, by another string object, by a sequence of characters, or by part of another string object. For example:

#include <string>
using namespace std;
void f()
{
const char text[] = "hello world";
string s1 = text; // initialization of string object with a C-style string
string s2(s1); // initialize with another string object
string s3(&text[0], &text[5]); // initialize by part of a C-string
string s4(10, 0); // by a sequence of 10 characters
string s5 ( s2.begin(), s2.find(' ')); // by part of another string object; s5 = "hello"
}
97. Never Store Arrays of Objects in an auto_ptr
The Standard Library's auto_ptr class template automatically destroys an object that is allocated on the free store when the current scope is exited. For example:

#include <memory> //definition of auto_ptr
#include <string>
using namespace std;

int main()
{
auto_ptr<string> ps (new string);
*ps = "hello";

} //ps is automatically destroyed at this point
Note however, that using auto_ptr to for an array of objects results in undefined behavior because the destructor of auto_ptr calls plain delete (and never delete []) to destroy its bound object. As you know, only operator delete[] should be used in order to destroy an array of dynamically-allocated objects. Therefore, never store arrays of objects in an auto_ptr:

int main()
{
auto_ptr<string> ps (new string[2]); //bad

} //undefined behavior
98. Efficiency of STL or Quality of Implementation?
I often hear people ask whether it's possible to write code that is quicker than STL.
This is a paradoxical question: the C++ Standard defines minimum performance requirements with which STL containers and algorithms must comply. This doesn't mean that one cannot do better than that, and in fact, there are many STL implementations that utilize sophisticated optimizations to provide better performance (see STLPort.org for such an example) than the Standard's minimum requirements. Thus, it all boils down to an issue called "quality of implementation." Put differently, a better, faster, and slimmer STL implementation is still STL as long as it meets all the ANSI/ISO requirements. Thus, if you're not satisfied with the performance of your default STL package, you may switch to another vendor's STL package without having to switch to another compiler or IDE.
99. Where are std::min() and std::max()?

The Standard Library defines the two template functions std::min() and std::max() in the <algorithm> header. In general, you should use these template functions for calculating the min and max values of a pair. Unfortunately, Visual C++ does not define these function templates. This is because the names min and max clash with the traditional min and max macros defined in <windows.h>. As a workaround, Visual C++ defines two alternative templates with identical functionality called _cpp_min() and _cpp_max(). You can use them instead of std::min() and std::max().To disable the generation of the min and max macros in Visual C++, #define NOMINMAX before #including <windows.h>.

100. Volatile Semantics and Container Objects
Volatile objects are used in multithreaded applications and applications that map hardware devices into registers. Although you can declare an STL container object with the volatile qualifier, you will not be able to use any of its member functions safely. The problem is that none of the STL containers declares any volatile member functions. Yet calling a non-volatile member function on a volatile object is ill-formed. Consider the following example:

volatile vector <int> vi;
vi.push_back(1); // trouble; push_back() isn't volatile
Some compilers will compile this code with warnings; others will issue an error message. Either way, the effect of calling a non-volatile member function on a volatile object is unpredictable because the state of the object might be changed by another thread while the function is executing.
Is there a way to get around this? Probably not. By design, STL containers don't include volatile member functions because of the performance penalty and implementation complexity associated with volatile semantics.