Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Oop Extract


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Oop Extract

  1. 1. Object Creation and Handling 11. Avoid calling virtual functions in constructors. Constructors do not support runtime polymorphism fully as the derived objects are not constructed yet when base class constructor executes. So, avoid calling virtual functions from base-class constructors, which might result in subtle bugs in the code. When inheritance is involved, when a derived class object is constructed, the base class constructor is called first and then the derived class constructors are called. Constructors are for initializing the object – the base class constructor is responsible for initializing the base class part of the object, and derived class constructors are responsible for initializing the derived class parts of the object. Invoking virtual methods inside constructors is not the same as calling virtual methods from other functions. Constructors are for initializing the object, for which the static type is known. Virtual functions are invoked on the dynamic type of the object, but the object might not be properly initialized while constructor call is made. Since the derived parts of the object wouldn’t be initialized while executing the base class constructor, calling any virtual methods from the base class constructor doesn’t work well. Depending on the language semantics different problems can happen, leading to subtle bugs in the code (which is covered in detail with examples later in this tip). How about the classes that aren’t inherited – is it acceptable to call virtual functions from the constructors of those classes? If the class is prevented from being inherited (for example, with private constructors or by declaring the class as non-inheritable), then there is no problem as the class cannot serve as a base class in future. The other possibility is that the class is inheritable and we make some virtual method calls in the constructor; in future, if that class is inherited in future, and the virtual method called is overridden in the new derived class, then it might introduce a subtle bug. Prefer providing direct initialization code in constructors [1]; do not depend on runtime polymorphism (calling virtual methods) inside constructors. Language Support, Features and Code Examples The semantics of calling virtual methods from base-class constructors in C++ is different from that of Java/C#. In C++, any virtual calls will be resolved only to the type of the object being constructed; however in Java/C#, a virtual method call will be made. Both the approaches have advantages and disadvantages; before covering those details, let us see an example from C++ and Java first which shows this difference: // C++ example struct base { base() { vfun(); } virtual void vfun() {
  2. 2. cout << “Inside base::vfunn”; } }; struct deri : base { virtual void vfun() { cout << “Inside deri::vfunn”; } }; int main(){ deri d; } // prints: // Inside base::vfun // Java example class base { public base() { vfun(); } public void vfun() { System.out.println(quot;Inside base::vfunquot;); } } class deri extends base { public void vfun() { System.out.println(quot;Inside deri::vfunquot;); } public static void main(String []s) { deri d = new deri(); } } // prints: // Inside deri::vfun In C++, when a virtual method call is made from the base class constructor, only the static type of the
  3. 3. object is considered. If the dynamic type was considered and the derived class method is invoked, then it will possibly result in accessing the un-initialized parts of the derived object, which is not desirable (this is what precisely happens in case of Java and C#). However, because of this semantics, sometimes it can happen that the users accidentally end up calling a pure virtual function in C++. Calling pure virtual function results in undefined behaviour; the usual behaviour for the compilers is to throw a runtime exception and terminate the program abnormally. Here is an example: struct base { base() { base * bptr = this; bptr->bar(); // even simpler ... ((base*)(this))->bar(); } virtual void bar() =0; }; struct deri: base{ void bar(){ } }; int main(){ deri d; } // g++ output: // pure virtual method called // ABORT instruction (core dumped) The compiler can detect virtual method calls inside the pure virtual function calls, but it cannot invocations detect indirect invocations. In case there is a temporary variable to have indirect invocation or a typecast present, then the compiler will not detect call to pure virtual function call. Note that, if Java/C# like semantics were followed in C++, the overridden method would have been called which would have avoided this possibility of invoking a pure virtual function. Coming to Java and C#, since the overridden method is called when a virtual method is called in a base class constructor, the program can end-up accessing the derived parts, which wouldn’t have been initialized yet. The following example shows this with an example: Derived class has a data member of type Integer, which will be initialized only when the constructor of Derived is invoked; however, since the virtual method foo is called from the base class constructor, the program tries to call toString method from i which is not initialized yet, resulting in NullPointerException.
  4. 4. // Java code class Base { public Base() { foo(); } public void foo() { System.out.println(quot;In Base's foo quot;); } } class Derived extends Base { public Derived() { i = new Integer(10); } public void foo() { System.out.println(quot;In Derived's foo quot; + i.toString()); } private Integer i; } class Test { public static void main(String [] s) { new Derived().foo(); } } // this program fails by throwing a NullPointerException An equivalent program in C# will also fail in the same way by trying to access the un-initialized data member in Derived object. [1] Invoking virtual methods in destructors - as in C++ - also doesn’t work well. The object is getting destroyed and hence the object cannot be expected to be in consistent state, so it will result in defects.