Not an experienced c++ programmer here and I've gotten myself a bit
stuck. I'm trying to implement a class lib and I've run into a sticky
problem that I can't solve. I'd appreciate any help that I can get!
Consider 3 classes in the following heirarchy:
base
/ \
deriv1 deriv2
\
deriv3
All derivations are public. These objects represent "values" and have
lots of const member functions that perform some transformation and
return a new, transformed object:
base x;
base y = x.foo();
Now foo() is a const member function so the way it has to work is to
create a new base object and return it. This seems simple enough but I
haven't found a good strategy for doing it in a way that simultaneously
avoids lots of code duplication and doesn't generate memory leaks. Here
are two strategies that I can think of:
=======================================
Strategy 1: Return all objects by value
The member function foo creates a new object on the stack and returns it
by value when needed. No memory leaks, and these are not huge objects so
there isn't much of a penalty for passing them by value.
CONS: The member function foo() can't be implemented in the base class
because it will cause object slicing when used in the derived classes.
So instead I implement:
deriv1 deriv1::foo() const;
and
deriv2 deriv2::foo() const;
The foo() operation is identical for deriv1 and deriv2 except that it
creates and returns a different object. So repeating the code is ugly
and hard to maintain. Furthermore I cannot allow objects of class deriv3
to inherit foo() because of slicing issues. It also causes a problem if
the deriv3 class has a unique member function that doesn't exist in
deriv1 or deriv2. Then the following code is also problematic:
deriv3 A;
deriv3 B = A.foo().unique(); //compile error!
=======================================
Strategy 2: Use references to objects, covariant return and object
factories to implement foo via inheritance
Now we implement foo() in the base class as:
virtual base& base::foo() const {
base& tmp =new_object();
// whatever foo does
return tmp;
}
// along with a base class object factory:
virtual base& new_object() {
// make a base object on
// the heap
return baseObject;
}
In each derived class (e.g. deriv2) we implement foo by using the base
class implementation:
deriv2& deriv2::foo() const {
return static_cast<deriv2&>(base::foo());
}
// we also require an object factory for deriv2 objects
deriv2& deriv2::new_object() {
// make a deriv2 object on
// the heap
return deriv2Object;
}
CONS: memory leaks like crazy. There are no easy ways to clean this up.
The callers can't even do it if they're diligent since there will be
many unnamed temporaries generated by expressions like:
deriv3 U;
double bar = U.foo().functionReturningDouble();
I don't think smart pointers will help, because returning smart pointers
will cause the covariant return paradigm to break.
=======================================
So what to do? The only thing I've been able to think of, is a scheme
where base and its derived objects actually assume ownership for any and
all objects created by member functions. For example:
deriv2 D;
deriv2 E = D.foo()
In this scheme D maintains a smart pointer to the new object but foo
returns a raw pointer as a reference. When D goes out of scope or is
deleted, its destructor cleans up any allocated objects that it owns.
What happens in cases like:
deriv2 F=D;
is a bit unclear to me, but I'm assuming I can work it out. But the
whole idea seems error-prone and it certainly isn't optimal in terms of
memory use.
Can anyone offer any suggestions?
-- Jeff Greenberg