Saturday, May 31, 2008

C++ #27

1.When does a virtual member function imposes runtime overhead?
Dynamic binding, for example, having a virtual member(s), is one of the basic principles of OO programming. If the compiler can resolve a call to a virtual member function statically, no extra overhead is incurred and it can even be inlined. In other words, it is as efficient as any ordinary function call, provided that the compiler can decide at compile time which object's virtual function is the one to be invoked. However, in certain circumstances it can't be done, and as a result, a virtual call imposes a slight run-time overhead, for example, when a virtual is called from a pointer or a reference to an object:

class V { //a polymorphic class
virtual void show() const { cout<<"I'm V<<endl; }
};

class W : public V {
void show() const { cout<<"I'm W<<endl; }
};

void f(V & v, V *pV) {

W w; //local object
w.show(); //call resolved statically to W::show(); no overhead.


v.show(); //v is a reference, can be bound to a V or object derived

//from V; call resolution is postponed to run-time.

pV->show(); //pV is a pointer to a base class, can point to a derived

//object as well, so call resolution is postponed to run-

//time.

}//end f
2.Default Arguments in Virtual Functions Must be Identical

You should pay attention when using default arguments in virtual functions. The default values in the overriding function have to be identical to the corresponding default values in the base class. Otherwise, unexpected results may occur:

class Base {
public: virtual void say (int n = 0) const { cout<<say(); //output is 0 and not 5!
It should be noted that unlike virtual functions, which are resolved at run time, default arguments are resolved statically, that is, at compiled time. In the example above, the static type of p is "pointer to Base". The compiler therefore supplies 0 as a default argument in a function call. However, the dynamic type of p is "pointer to Derived", which takes 5 as a default argument.
Since a base class and a derived class may be compiled separately, most compilers cannot detect such inconsistencies. Therefore, the programmer is responsible for making sure that the default values of a virtual function in all levels of derivation match exactly.

3.Return type of an overriding virtual member function

Once declared virtual, a member function can be overridden in any level of derivation, as long as the overriding version has the identical signature of the original declaration. This is quite straightforward, but sometimes a virtual has to return an object of type that differs from the return type declared in its base class. The C++ Standard allows that only when the return type is replaced with a class publicly derived from the original return type. For example:

class B {
virtual B* clone () const { return new B; }
}

class D {
void* clone () const { return new B; } //error; return type
//differs

D* clone () const { return new D; } //OK, D is publicly
//derived from B

}
4.Virtual Member Functions Should Not Be Private
There is not much point in declaring a virtual member function as private. A private virtual function cannot be overridden in a derived class. Furthermore, it is customary to extend virtual functions in a derived class by first invoking the base class' version of that function, and then extend it with additional functionality. Again, this can't be done when a virtual function is declared private


5.When to Use a Pure Virtual Member Function
A pure virtual function is merely an interface and can be thought of as a way to enforce policy. A pure virtual function should be used when subclasses implement the same interface completely differently. For example, a persistence interface can have a write operation to store the state of an object. Yet, each class that implements this interface performs individual operations to store its state into a file:

class persistent {
public:
virtual void Write () = 0;
};
class Clock: public persistent {
public:
void Write() {_} // implement the interface
};
class Date: public persistent {
public:
void Write() {_} // implement the interface
};
6.Converting a Virtual Member Function to a Pure Virtual One in a Derived Class
In general, a derived class that inherits a pure virtual member function from its abstract base class implements this function. The opposite is also true: a virtual member function that is implemented in a base class can become pure virtual in a derived class. For example:

class Base
{
virtual void func() { /*do something */ }
};

class Derived : public Base
{
virtual void func() = 0; // redefined as pure virtual
};
You don't normally convert a virtual member function to a pure virtual in a derived class, however, sometimes it is the only way to overcome flaws in a base class that you are not allowed to fix but have to inherit from.

7.Preventing Program's Crash due to a Pure Virtual Function's Call
A pure function should never be invoked. However, a buggy program or a buggy compiler can sometimes call a pure virtual function. In general, when a pure virtual function is invoked, the program crashes and you cannot continue debugging it. To prevent the program from crashing, you can implement the pure virtual function. This makes debugging possible even when a pure virtual function is inadvertently called.

#include <iostream>
using namespace std;
class Dog{
public:
virtual void Bark()=0; //pure virtual, should never be invoked
};
// the following definition ensures that the pure virtual call doesn't crash
void Dog::Bark(){
cout<<"Dog::Bark called";
}
class Beagle : public Dog{
public:
virtual void Bark() {cout<<"Beagle"; }
};

int main(){
Beagle* m_pSnoopy = new Beagle;
/* The following should call Beagle::Bark(); a buggy compiler/program calls Dog::bark() instead */
m_pSnoopy->Bark();
}

When you have fixed the bug that calls the pure virtual function (or upgrades the faulty compiler), remember to delete the implementation of the pure virtual function.
8. Inlining Virtual Member Functions
Generally, compilers can't inline a virtual function call if the it's resolved dynamically. Therefore, declaring a virtual member function inline might seem pointless. However, not every call of a virtual function is resolved dynamically; in some cases, the compiler can resolve the call statically, as if the function weren't virtual. In situations like these, the compiler can also inline the call. For example:


class Base
{
public:
inline virtual int f() { return 0; }
};

int main()
{
Base b;
b.f(); // resolved statically; call can be inlined
}
The invocation of f() is resolved statically because b is not a pointer or a reference. The compiler can also expand the call inline, thereby optimizing the code even further.
9.Signatures of Pure Virtual Functions

Although pure virtual functions are mere placeholders, you cannot change their signature and return type when you implement their overriding versions in a derived class (the only exception is the covariant return type, which allows a virtual function's return type to co-vary with the type of its class). For example:

class Array
{
public:
virtual int get_index()=0; // pure virtual
};

class Int_array: public Array
{
public:
virtual int get_index(int n); // error: signature mismatch
};

10.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;
}
11.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.

No comments: