Tips On Object Oriented Programming
Tips On Object Oriented Programming
Essential Techniques
S G Ganesh
sgganesh@gmail.com
Language Pragmatics
// prints:Inside base::vfun
Java/C# resolves virtual function calls dynamically
// Java example
class base {
public base() {
vfun();
}
public void vfun() {
System.out.println("Inside base::vfun");
}
}
class deri extends base {
public void vfun() {
System.out.println("Inside deri::vfun");
}
public static void main(String []s) {
deri d = new deri();
}
}
Java/C# resolves virtual function calls dynamically
// Java example
class base {
public base() {
vfun();
}
public void vfun() {
System.out.println("Inside base::vfun");
}
}
class deri extends base {
public void vfun() {
System.out.println("Inside deri::vfun");
}
public static void main(String []s) {
deri d = new deri();
}
}
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;
}
In C++, pure virtual methods might get called
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)
Dynamic method call in Java might lead to trouble
// Java code
class Base {
public Base() {
foo();
}
public void foo() {
System.out.println("In Base's foo ");
}
}
class Derived extends Base {
public Derived() {
i = new Integer(10);
}
public void foo() {
System.out.println("In Derived's foo " + i.toString() );
}
private Integer i;
}
class Test {
public static void main(String [] s) {
new Derived().foo();
}
}
Dynamic method call in Java might lead to trouble
// Java code
class Base {
public Base() {
foo();
}
public void foo() {
System.out.println("In Base's foo ");
}
}
class Derived extends Base {
public Derived() {
i = new Integer(10);
}
public void foo() {
System.out.println("In Derived's foo " + i.toString() );
}
private Integer i;
}
class Test {
public static void main(String [] s) {
new Derived().foo();
}
}
// this program fails by throwing a NullPointerException
2. Preserve the basic properties of methods while
overriding
struct Base {
virtual void call(int val = 10)
{ cout << “The default value is :”<< endl; }
};
// user code:
Base *b = new Derived;
b->call();
In C++, provide consistent default parameters
struct Base {
virtual void call(int val = 10)
{ cout << “The default value is :”<< endl; }
};
// user code:
Base *b = new Derived;
b->call();
// prints:
// The default value is: 10
In Java, final might be removed while overriding
class Base {
public void vfoo(final int arg) {
System.out.println("in Base; arg = "+arg);
}
}
class Derived extends Base {
public void vfoo(int arg) {
arg = 0;
System.out.println("in Derived; arg = "+arg);
}
public static void main(String []s) {
Base b = new Base();
b.vfoo(10);
b = new Derived();
b.vfoo(10);
}
}
In Java, final might be removed while overriding
class Base {
public void vfoo(final int arg) {
System.out.println("in Base; arg = "+arg);
}
}
class Derived extends Base {
public void vfoo(int arg) {
arg = 0;
System.out.println("in Derived; arg = "+arg);
}
public static void main(String []s) {
Base b = new Base();
b.vfoo(10);
b = new Derived();
b.vfoo(10);
}
}
// prints:
// in Base; arg = 10
// in Derived; arg = 0
Provide consistent exception specification
struct Shape {
// can throw any exceptions
virtual void rotate(int angle) = 0;
// other methods
};
// client code
Shape *shapePtr = new Circle();
shapePtr->rotate(10);
// program aborts!
3. Beware of order of initialization problems.
// translation unit 1
int i = 10;
// translation unit 2
extern int i;
int j = i;
// j is 0 or 10?
// depends on the compiler/link line.
In Java, such init can cause unintuitive results
class Init {
static int j = foo();
static int k = 10;
static int foo() {
return k;
}
public static void main(String [] s) {
System.out.println("j = " + j);
}
}
In Java, such init can cause unintuitive results
class Init {
static int j = foo();
static int k = 10;
static int foo() {
return k;
}
public static void main(String [] s) {
System.out.println("j = " + j);
}
}
// prints
// j=0
4. Avoid switch/nested if-else based on types
// C++ Code
int x, y; // global variables x and y
struct Point {
int x, y; // class members x and y
Point(int x, int y); // function arguments x and y
};
C++/Java/C# example for a bug with hiding
// C++
Point(int x, int y) {
this->x = x;
this->y = y;
}
// Java and C#
Point(int x, int y) {
this.x = x;
this.y = y;
}
C++: No overloading across scopes
struct Base {
void foo(int) {
cout<<"Inside Base::foo(int)";
}
};
Derived d;
d.foo(10);
C++: No overloading across scopes
struct Base {
void foo(int) {
cout<<"Inside Base::foo(int)";
}
};
Derived d;
d.foo(10);
// prints:
// Inside Derived::foo(double)
Java: Overloading across scopes!
class base {
public void foo(int i) {
System.out.println("In Base::foo(int)");
}
}
class base {
public void foo(int i) {
System.out.println("In Base::foo(int)");
}
}