Thursday, May 29, 2008

C++ puzzles #5

Limitations of reinterpret_cast


A reader tried to cast a nonstatic member function of a certain class to void*, as in the following example:

 
class A
{
public:
 void func();
};
 
void *pmf = reinterpret_cast < void *> (&A::func); // error


However, the compiler refused to accept this code. Why was the casting of a pointer to member to void * impossible, even with reinterpret_cast? This is not a compiler's whim. A pointer to member function cannot be converted to void * because the former is not a real pointer. Rather, in most cases it's a data structure that contains offsets and addresses that enable the implementation to access a function of a given object. Casting it to void * is technically impossible and therefore not allowed.

Initializing a Static Array Member of a Class


You can initialize static arrays member of a class. The initialization must appear at the place of definition, not inside the class body. For example:

 
class A
{
private:
 static char vars[2]; // declaration, can't initialize here
//..
};
char A::vars[2] = {'a', 'b'}; // definition + initialization

Internal and External Linkage


Global inline functions, consts and typedefs have internal linkage.

That means that the declarations in these two files do not interfere with each other:

 
               //File1.c
               typedef int T;
               const int a = 12;
               inline T f (int i)
               { return i+a; }
 
               //File2.c
               typedef void T;
               const int a = 8;                                                    
inline T f (double d)
               { cout  <<  d; }


A const can be given external linkage by an explicit declaration:

 
               //File3.c                                                                
extern const int a;                                const int a = 77;
 
               //File4.c
               extern const int a;
               void g () { cout << a; }
 
               Here, g () will print 77.

char[] vs. char *


C++ forbids direct assignments of arrays. Therefore, your compiler will flag the following code as an error:

 
char buff[];
buff=“hello”; // illegal, attempt to assign arrays


Now suppose we change the definition of buff to:

 
char * buff; 


This time, assignment is allowed:

 
buff=“hello”; // fine, buff is a pointer


Why is this? Because buff is a pointer, not an array, you may assign a new value to it. The assignment takes the memory address of the quoted string and assigns it to buff. However, the array elements are not copied.

Constructor Call Sequence


The constructors in a class hierarchy execute in the following sequence: base class constructors are called first, then member object's constructors, and finally, the constructor of the derived class executes. If a class has multiple base classes, the base classes' constructors execute right to left, according to their position in the base class list. For example:

 
struct C: public A, public B
{
 C() {}
private:
 F f;
};
 
int main()
{
 C c;
}


When c is instantiated, the constructors are called in the following sequence: A(), B(), F(), C(). Constructors of virtual base classes execute before any other non-virtual base class constructors. Consider:

 
struct M: public A, public B, virtual public V
{
};


In this example, the constructors are called in the following sequence: V(), A(), B(), M(). Destructors are always executed in the reverse order of constructor calls. Therefore, when M is destroyed, the destructors execute in the following order: ~M(), ~B(),~A(),~V().

Virtual Base Classes Must Have a Default Constructor


Virtual inheritance imposes several restrictions. One of them is that you cannot use a class that has no default constructor as a virtual base class. Consider the following program:

 
struct A
{
 A(int n) {} // no default ctor
};
struct B: public virtual A // virtual inheritance
{
 B(): A(5) {}
};
struct C: public B
{};
int main()
{
 C c; //compilation error: "Cannot find default constructor
      //to initialize base class 'A'."
}


Had we used plain inheritance instead of virtual inheritance, this program would compile without a hitch. Why does C++ mandate that a virtual base class have a default constructor? The problem is that only a single instance of a virtual base class exists in a hierarchy. If A served as a virtual base of several classes, each of which passing a different argument to A's constructor, the compiler wouldn't know which argument list to choose. This problem doesn't exist with non-virtual base classes because there may be multiple instances of the base subobject in this case.

 

No comments: