Saturday, May 31, 2008

C++ #14

30. Simulating Virtual Constructors

A constructor may not be declared virtual. The easiest way to simulate virtual construction is by defining a virtual member function that returns a constructed object of its class type:

class Browser{
public:
virtual Browser* construct() { return new Browser; } //virtual default constructor
virtual Browser* clone() { return new Browser(*this); } //virtual copy constructor
};

class HTMLEditor: public Browser{
public:
HTMLEditor * construct() { return new HTMLEditor; }//virtual default constructor
HTMLEditor * clone() { return new HTMLEditor (*this); } //virtual copy constructor
};
The polymorphic behavior of the member functions clone() and construct() enables you to instantiate a new object of the right type, without having to know the exact type of the source object.

void create (Browser& br)
{
Browser* pbr = br.construct();
//…use pbr and br
delete pbr;
}
31. Access Specification of a Virtual Method Should not Change in a Derived Class
The access specification of a public virtual member function defined in a base class, can be changed in a derived class:

class Base {
public:
virtual void Say() { cout<<"Base";}
};
class Derived : public Base {
private: //access specifier changed; legal but not a very good idea
void Say() {cout <<"Derived";} //overriding Base::Say()
};
Although this is legal, it will not work as expected when pointers or references are used: A pointer or reference to Base, can also be assigned to any object derived from Base:

Derived d;
Base *p = &d;
p->Say(); //OK, invokes Derived::Say()
Since the actual binding of a virtual member function is postponed to runtime, the compiler cannot detect that a non-public member function will be called: it assumes that p points to an object of type Base, in which Say() is a public member. Therefore, you should not override the access specification of virtual member function in a derived class.

32. Qualified Calls
A function call such as the following:

classname::funcname();
is a qualified call. Qualified disambiguate function names. They are needed, for instance, in a class that inherits from several base classes, each of which having functions with identical names. Consider:

struct B1
{
int f();
};
struct B2
{
int f();
};
class D: public B1, public B2
{
public: // f() is ambiguous in class D
int g() {B2::f();} //qualified call resolves the right f()
};
Another use of qualified calls is to bypass the virtual call resolution mechanism. Suppose you have a virtual function f() in a base class that is overridden in a derived class. To invoke the base's f() in the derived class, you need to use the qualified call base::f(). In this case, the call is resolved statically, not dynamically.

Overloading
33. Hiding a base class member function

A derived class may hide a member function declared in its base class by using the same function name, but with a different signature, or list of arguments. Usually, this case suggests a programmer's mistake, especially if the hidden function is virtual. However, it can also be used as a means of hiding a base class member function when the designer of the derived class decides that any invocation of the original member function is undesirable or even dangerous:

class B {
private:
FILE *pf;
public:
//...
virtual void close(FILE *f) //may throw an exception
};

class D : public B { //no file I/O in D, calling B::close from
//here is dangerous and never needed.
public:
//a 'neutralized' close, harmless
void close(int i){} //hiding B::close(), not overriding it
};


void f()
{
B b;
FILE *p;
//....
D d;
d.close(p);//compile-time error: B::close()not accessible

}
34. Use private copy constructor and assignment operator to avoid object copying
If you don't want users of your class to be able to assign objects of its type (password string objects are a good example), you can declare a private assignment operator and copy constructor. Please note that the compiler-synthesized copy constructor and assignment operator are public, therefore, you have to define them explicitly as private members in this case. For example:

class NoCopy {
private:
NoCopy& operator = (const NoCopy& other) { /*..*/}
NoCopy(const NoCopy& other) {/*..*/}
public:
//...
};

void f()
{
NoCopy nc; //fine, default constructor called
NoCopy nc2(nc); //compile time error; attempt to call a private copy constructor
nc2 = nc; //also a compile time error; operator= is private
}
Pointer
35. Constant pointers
There are cases when you need to define a constant pointer to a variable/object; for instance, when taking a function address, or when you want to protect a pointer from unintended modifications such as assignment of new address, pointer arithmetic, etc. In fact, an object's this is a constpointer. A constant pointer is declared:
int j = 10;
int *const cpi = &j; //cpi is a constant pointer to an int
*cpi = 20; //OK, j is assigned a new value
cpi++; //Error; can not change cpi
If you're confused by the syntax, remember: const defines a constant pointer, whereas a const variable is declared like this:
const int k = 10; //j's value may not be changed
And a const pointer to a const variable:
int *const cpi = &k; //cpi is a constant pointer to a const int

*cpi = 20; //Error; k's value cannot be modified
cpi++; //Error; can not modify a const pointer

36. Accessing the Addresses of Class's Member Functions
You can take the addresses of a class's static and non-static member functions. However, the class's constructor(s) and destructor are an exception to the rule. You cannot take their addresses nor can you invoke these members through a callback mechanism
37. Introducing Pointers to Members
Pointers to members are one of the most intricate syntactic constructs in C++, and yet, they are a very powerful feature too. In future tips, I will discuss pointers to members in further detail and show to define and use them.
38. Uses of Pointers to Members
Pointers to members enable you to invoke a member function of an object without having to know the name of that function. This is very handy if you're implementing a callback mechanism. Likewise, you can use a pointer to data member to examine and alter the value of a data member without knowing its name. Thus, you can think of pointers to members as the object-oriented equivalents of function pointers and data pointers.
39. Pointers to Member Functions and Pointers to Data Members
A class can have two general categories of members: function members and data members. Similarly, there are two categories of pointers to members: pointers to member functions, and pointers to data members. The latter are less common because in general, classes do not have public data members. However, when using legacy C code that contains structs or classes that happen to have public data members, pointers to data members are useful.

No comments: