Tuesday, March 17, 2009

Avoiding pointers

I often see a C++ code that uses pointers in situations, where they're not needed. It seems that we often easily give up when we encounter slightly irregular code construct, where typical reference usage is not obvious. Consider following situation:


class A {
public:
virtual void action() = 0;
};
class B : public A {
public:
B() {}
void action() { /* action code */ }
};

class HolderClass {
public:
void setInstance(A * newInstance) {
instance = newInstance;
}
void doSomething() {
instance->action();
}
private:
A * instance;
};

int main() {
B myInstance;
HolderClass c;
c.setInstance(&myInstance);
}


The general rule is: to avoid pointers, allow the compiler to arrange things in RAII way. Use references, initialize them as fast as you can (i.e. in base initializers):
   
class A {
public:
virtual void action() = 0;
};
class B : public A {
public:
B() {}
void action() { /* action code */ }
};
class HolderClass {
public:
HolderClass(A & newInstance) : instance(newInstance) {}
void doSomething() {
instance.action();
}
private:
A & instance;
};

int main() {
B myInstance;
HolderClass c(myInstance);
}


However, both of above approaches share the same issue: the ownership issue. Consider following scenario:

HolderClass* fun() {
B myInstance;
HolderClass* c = new HolderClass(myInstance);
/* Or:
HolderClass* c = new HolderClass();
c->setInstance(&myInstance);
*/
return c;
}


Possible pointer-approach solutions are:
  1. Use shared pointer instead of regular pointer.
  2. Take a pointer and copy the object, storing pointer to newly created instance.

But how to solve this without using pointers? Generally, we must copy the object before binding it to the reference member. If A would not have been an abstract class, and the C++0x standard Rvalue references would have been already available, then following solution would be possible:

class HolderClass {
public:
HolderClass(A & newInstance) : instance(A(newInstance)) {
}
private:
A && instance;
};


But A is an abstract class, and we may not want to use unfinished standard features. Is it possible to make a copy of A* in such a way, that it would be available in base initializer list? It is! Thanks to the fact, that you can bind (after dereferencing) pointer returned by new() to an reference variable. Below you can see two possible solutions:

class A {
public:
virtual void action() = 0;
};
class B : public A {
public:
B() {}
void action() { }
};

class C : public A {
public:
B() {}
void action() { }
};
A & copyOf(A & a) {
if( typeid(a) == typeid(B)) {
B * b = new B( *dynamic_cast<B*>(&a) );
return *b;
} else if( typeid(a) == typeid(C)) {
C * c = new C( *dynamic_cast<C*>(&a) );
return *c;
}
}

class HolderClass {
public:
HolderClass(A & newInstance) :
instance(copyOf(newInstance)) {}
~HolderClass() { delete &instance; }
void doSomething() {
instance.action();
}
private:
A & instance;
};

int main() {
B myInstance;
HolderClass c(myInstance);
}


Next solution follows the virtual copy constructor idiom:

class A {
public:
...
virtual A& clone() = 0;
...
};
class B : public A {
public:
...
virtual A& clone() { return *new B(*this); }
...
};

class HolderClass {
public:
HolderClass(A& newInstance) :
instance(newInstance.clone()) {}
~HolderClass() { delete &instance; }

void doSomething() {
instance.action();
}
private:
A& instance; // this can be A&&, A&, A*,
// auto_ptr<A>, etc (any of them would work in your
// case)

};


Notice the delete calls in ~HolderClass() destructors — binding to an reference variable doesn't have anything to do with managing of memory lifetime.

The copyOf() approach is non-intrusive and can be easily used with third-party classes which cannot be modified. Otherwise the clone() approach is probably cleaner.


read more!