Connecting Tech Pros Worldwide Forums | Help | Site Map

Inheritance and object factories...HELP!!

Jeff Greenberg
Guest
 
Posts: n/a
#1: Jul 22 '05
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

Dave Townsend
Guest
 
Posts: n/a
#2: Jul 22 '05

re: Inheritance and object factories...HELP!!


Jeff,

I don't think I fully understand your problem without an actual working
piece of code, but from what I see you might benefit from the "pimpl" idiom
here.

In the pimpl idiom you would have a Concrete class which contains a member
variable
_pimpl which is a pointer to an implementation class. The functionality of
the
concrete class is provided by forwarding calls to the implementation class:

class Concrete : public Foo
{
public:
Concrete(){ code to choose which deriv object to create } ;
~Concrete()
{
delete _pimpl;
}
virtual void doFoo( ) { _pimpl->doFoo() ;}
private:
Foo* _pimpl;
};

Deriv1, dervi2, deriv3,
class deriv1 : public Foo
{
public:
virtual void doFoo(){ }

};
// etc, etc

This pattern now allows you to vary the implementation class by using a
different derived
object for the _pimpl. In this way you can return an actual object of class
"concrete" which
has the actual behavior of the various derived objects deriv1, deriv2,
deriv3 etc. Since you
return an actual object, there is no issue about memory leaks, slicing, etc.
( you said somewhere
that the construction of the deriv classes was not expensive, so the copying
semantics would
be ok to implement in Concrete ). If you client is unaware of the actual
type of object (deriv1,
deriv2, etc) and only cares about the public interface of the base class,
this should work.

Does that help? Let me know if I can explain anything better,

dave.


"Jeff Greenberg" <jgreenb2@comcast.net> wrote in message
news:jgreenb2-BA56F5.19065217102004@news.supernews.com...[color=blue]
> 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[/color]


Alf P. Steinbach
Guest
 
Posts: n/a
#3: Jul 22 '05

re: Inheritance and object factories...HELP!!


* Jeff Greenberg:[color=blue]
>
> 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();[/color]

What you mean by "values" seems to be immutable objects.


[color=blue]
> 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.[/color]

Well that isn't quite correct.

One way is to use a protected template function:

class Base
{
protected:
template< class Result >
Result foo_() const
{
return Result();
}
public:
virtual ~Base() {}
virtual Base foo() const { return foo_<Base>(); }
};

class Derived: public Base
{
public:
virtual Derived foo() const { return foo_<Derived>(); }
};

Another way is to use dynamic allocation via a protected virtual
factory function. To avoid memory leaks the foo() result should
then be a smart-pointer, the handle/body-pattern. Smart-pointers
can work with covariance when they're not reseatable. From the
rest of what you write I presume you're aware of why it's not a
good idea with reseatable smart-pointers in this context. However,
an easier solution is to note that anybody wanting a Derived-specific
result know they're dealing with Derived and so can call a Derived-
specific version of foo; this even works with std::auto_ptr:

class Base
{
private:
virtual Base* rawFoo() const { return new Base; }
public:
typedef std::auto_ptr<Base> AutoPtr;

virtual ~Base() {}
AutoPtr foo() const { return AutoPtr( rawFoo() ); }
};

class Derived: public Base
{
private:
virtual Derived* rawFoo() const { return new Derived; }
public:
typedef std::auto_ptr<Derived> AutoPtr;

AutoPtr derivedFoo() const{ return AutoPtr( rawFoo() ); }
};

int main()
{
Derived().foo(); // calls Derived::rawFoo, Base::AutoPtr
Derived().derivedFoo(); // ditto, but now Derived::AutoPtr
}

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jeff Greenberg
Guest
 
Posts: n/a
#4: Jul 22 '05

re: Inheritance and object factories...HELP!!


Alf,

Thanks for the reply. I looked at your first suggestion:

In article <4173098a.407274312@news.individual.net>,
alfps@start.no (Alf P. Steinbach) wrote:
[color=blue][color=green][color=darkred]
>> >[/color]
> > 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.[/color]
>
> Well that isn't quite correct.
>
> One way is to use a protected template function:
>
> class Base
> {
> protected:
> template< class Result >
> Result foo_() const
> {
> return Result();
> }
> public:
> virtual ~Base() {}
> virtual Base foo() const { return foo_<Base>(); }
> };
>
> class Derived: public Base
> {
> public:
> virtual Derived foo() const { return foo_<Derived>(); }
> };
>[/color]

Unfortunately, this does not compile (at least on any compiler I have)
because
[color=blue]
> virtual Derived foo() const { return foo_<Derived>(); }[/color]

is an illegal override of
[color=blue]
> virtual Base foo() const { return foo_<Base>(); }[/color]

since Base and Derived are not covariant. Of course it would work if we
were returning Base& and Derived& but that leads back to my original
problem. Did you have something else in mind?
[color=blue]
> Another way is to use dynamic allocation via a protected virtual
> factory function. To avoid memory leaks the foo() result should
> then be a smart-pointer, the handle/body-pattern. Smart-pointers
> can work with covariance when they're not reseatable. From the
> rest of what you write I presume you're aware of why it's not a
> good idea with reseatable smart-pointers in this context.[/color]

You give me more credit than I deserve. I'm not sure how
"non-reseatable" (is that the same as "non-resetable"?) smart pointers
can help. To be covariant, the return type must be a pointer to a
derived type of the base class return. The smart pointer libs that I'm
aware of all work through templates. For example:

boost::shared_ptr<Base> has no inheritance relationship with
boost:shared_ptr<Derived>. So how does this help?
[color=blue]
> However,
> an easier solution is to note that anybody wanting a Derived-specific
> result know they're dealing with Derived and so can call a Derived-
> specific version of foo; this even works with std::auto_ptr:
>
> class Base
> {
> private:
> virtual Base* rawFoo() const { return new Base; }
> public:
> typedef std::auto_ptr<Base> AutoPtr;
>
> virtual ~Base() {}
> AutoPtr foo() const { return AutoPtr( rawFoo() ); }
> };
>
> class Derived: public Base
> {
> private:
> virtual Derived* rawFoo() const { return new Derived; }
> public:
> typedef std::auto_ptr<Derived> AutoPtr;
>
> AutoPtr derivedFoo() const{ return AutoPtr( rawFoo() ); }
> };
>
> int main()
> {
> Derived().foo(); // calls Derived::rawFoo, Base::AutoPtr
> Derived().derivedFoo(); // ditto, but now Derived::AutoPtr
> }[/color]

This would work for many applications but in this application it leads
to unacceptable syntax in the calling program. Operations like "foo()"
represent generic mathematical transformations that are logically
identical so it would not make much sense to write:

Base().sqrt();

and then

Derived().derivedSqrt();

when both operations imply the same mathematical transformation.
Requiring such syntax would lead to a user revolt :)

-- Jeff
Alf P. Steinbach
Guest
 
Posts: n/a
#5: Jul 22 '05

re: Inheritance and object factories...HELP!!


* Jeff Greenberg:[color=blue]
> Alf,
>
> Thanks for the reply. I looked at your first suggestion:
>
> In article <4173098a.407274312@news.individual.net>,
> alfps@start.no (Alf P. Steinbach) wrote:
>[color=green][color=darkred]
> >> >
> > > 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.[/color]
> >
> > Well that isn't quite correct.
> >
> > One way is to use a protected template function:
> >
> > class Base
> > {
> > protected:
> > template< class Result >
> > Result foo_() const
> > {
> > return Result();
> > }
> > public:
> > virtual ~Base() {}
> > virtual Base foo() const { return foo_<Base>(); }
> > };
> >
> > class Derived: public Base
> > {
> > public:
> > virtual Derived foo() const { return foo_<Derived>(); }
> > };
> >[/color]
>
> Unfortunately, this does not compile (at least on any compiler I have)
> because
>[color=green]
> > virtual Derived foo() const { return foo_<Derived>(); }[/color]
>
> is an illegal override of
>[color=green]
> > virtual Base foo() const { return foo_<Base>(); }[/color]
>
> since Base and Derived are not covariant. Of course it would work if we
> were returning Base& and Derived& but that leads back to my original
> problem. Did you have something else in mind?[/color]

No it was just sloppy thinking in a hasty reply. Full points on creativity,
zero on attention to detail. The following slight adjustment compiles (;-)):


#include <iostream>
#include <memory>

class Base
{
protected:
template< class Result >
Result foo_() const
{
return Result();
}
public:
virtual ~Base() {}
virtual Base foo( Base* = 0 ) const { return foo_<Base>(); }
};

class Derived: public Base
{
public:
virtual Derived foo( Derived* = 0 ) const
{ return foo_<Derived>(); }

virtual Base foo( Base* ) const
{ return foo( static_cast<Derived*>(0) ); }
};

int main()
{
Base().foo();
Derived().foo();
}


The implementation of Derived::Foo(Base*) in terms of Derived::foo(Derived*)
is to avoid O(n^2) overrides in an n-classes inheritance chain. It does
involve a lot of object creation and destruction, though, modulo optimization.
However as I understood it that was OK.

[color=blue][color=green]
> > Another way is to use dynamic allocation via a protected virtual
> > factory function. To avoid memory leaks the foo() result should
> > then be a smart-pointer, the handle/body-pattern. Smart-pointers
> > can work with covariance when they're not reseatable. From the
> > rest of what you write I presume you're aware of why it's not a
> > good idea with reseatable smart-pointers in this context.[/color]
>
> You give me more credit than I deserve. I'm not sure how
> "non-reseatable" (is that the same as "non-resetable"?) smart pointers
> can help. To be covariant, the return type must be a pointer to a
> derived type of the base class return. The smart pointer libs that I'm
> aware of all work through templates. For example:
>
> boost::shared_ptr<Base> has no inheritance relationship with
> boost:shared_ptr<Derived>. So how does this help?[/color]

Uh, hm, well... Checking it out it's no good to return a temporary as a
reference to const because function return value is one of the exceptions to
the general rule that the temporary should persist. Dang!

But hey, now it seems clear why Andrei was asking about lifetime of
temporaries created for arguments.

Temporary storage is in general not good enough for a reference result because
that result might be passed back up the function call chain, or bound to
a local reference, or whatever. That's one of those situations where the
language isn't exactly helpful. The FAQ's solution to such dilemmas is to
use a comment with a stern warning about what the client should not ever do.

In the following code I haven't addressed the issue of factoring out the
common foo() functionality, because you don't specify anything about that.

Nor does it ensure that all access is via smart-pointers, because that
depends on the concrete design, and would just clutter things here, and
most of all, this is untested code -- it compiles, but that's it... ;-)


#include <memory>

class Base
{
protected:
class TempPtrStorage;

public:
class Ptr
{
friend class TempPtrStorage;
private:
Ptr( Ptr const& ); // Nope.
Ptr& operator=( Ptr const& ); // Nope.

protected:
std::auto_ptr<Base> myP;

Ptr( Base* p ): myP( p ) {}
public:
Base const* operator->() const { return myP.get(); }
};

protected:
class TempPtrStorage
{
friend class Base;
private:
mutable std::auto_ptr<Ptr> myStorage; // Could/should be optimized.
protected:
template< class PtrType, class Contained >
PtrType& smartPointer( Contained* pContained ) const
{
myStorage.reset( new PtrType( pContained ) );
return *static_cast<PtrType*>( myStorage.get() );
}
};

template< class PtrType, class Contained >
PtrType& smartPointer( TempPtrStorage const& storage, Contained*
pContained ) const
{
return storage.smartPointer<PtrType>( pContained );
}

public:
// Bind the result to a ref, or take its address, then you're fired.
virtual Base::Ptr const& foo(
TempPtrStorage const& stg = TempPtrStorage()
) const
{
return smartPointer<Base::Ptr>( stg, new Base() );
}
};

class Derived: public Base
{
public:
class Ptr: public Base::Ptr
{
friend class TempPtrStorage;
protected:
Ptr( Derived* p ): Base::Ptr( p ) {}
public:
Derived const* operator->() const
{
return static_cast<Derived*>( myP.get() );
}
};

// Bind the result to a ref, or take its address, then you're fired.
virtual Derived::Ptr const& foo(
TempPtrStorage const& stg = TempPtrStorage()
) const
{
return smartPointer<Derived::Ptr>( stg, new Derived() );
}
};

int main()
{
Derived d;
Base& bRef = d;

bRef.foo(); // Calls Derived::foo.
}



[color=blue][color=green]
> > However,
> > an easier solution is to note that anybody wanting a Derived-specific
> > result know they're dealing with Derived and so can call a Derived-
> > specific version of foo; this even works with std::auto_ptr:
> >
> > class Base
> > {
> > private:
> > virtual Base* rawFoo() const { return new Base; }
> > public:
> > typedef std::auto_ptr<Base> AutoPtr;
> >
> > virtual ~Base() {}
> > AutoPtr foo() const { return AutoPtr( rawFoo() ); }
> > };
> >
> > class Derived: public Base
> > {
> > private:
> > virtual Derived* rawFoo() const { return new Derived; }
> > public:
> > typedef std::auto_ptr<Derived> AutoPtr;
> >
> > AutoPtr derivedFoo() const{ return AutoPtr( rawFoo() ); }
> > };
> >
> > int main()
> > {
> > Derived().foo(); // calls Derived::rawFoo, Base::AutoPtr
> > Derived().derivedFoo(); // ditto, but now Derived::AutoPtr
> > }[/color]
>
> This would work for many applications but in this application it leads
> to unacceptable syntax in the calling program. Operations like "foo()"
> represent generic mathematical transformations that are logically
> identical so it would not make much sense to write:
>
> Base().sqrt();
>
> and then
>
> Derived().derivedSqrt();
>
> when both operations imply the same mathematical transformation.
> Requiring such syntax would lead to a user revolt :)[/color]

Well I think you could just use the dummy argument trick exemplified
at top, to call both functions 'sqrt' (differentiating on argument type
instead of name, where the client code never uses that argument).

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jeff Greenberg
Guest
 
Posts: n/a
#6: Jul 22 '05

re: Inheritance and object factories...HELP!!


In article <41744f62.490690796@news.individual.net>,
alfps@start.no (Alf P. Steinbach) wrote:
<......snip........>[color=blue]
>
> No it was just sloppy thinking in a hasty reply. Full points on creativity,
> zero on attention to detail. The following slight adjustment compiles (;-)):
>
>
> #include <iostream>
> #include <memory>
>
> class Base
> {
> protected:
> template< class Result >
> Result foo_() const
> {
> return Result();
> }
> public:
> virtual ~Base() {}
> virtual Base foo( Base* = 0 ) const { return foo_<Base>(); }
> };
>
> class Derived: public Base
> {
> public:
> virtual Derived foo( Derived* = 0 ) const
> { return foo_<Derived>(); }
>
> virtual Base foo( Base* ) const
> { return foo( static_cast<Derived*>(0) ); }
> };
>
> int main()
> {
> Base().foo();
> Derived().foo();
> }
>
>
> The implementation of Derived::Foo(Base*) in terms of Derived::foo(Derived*)
> is to avoid O(n^2) overrides in an n-classes inheritance chain. It does
> involve a lot of object creation and destruction, though, modulo optimization.
> However as I understood it that was OK.
>
>[/color]

Very clever! I've never seen that dummy argument trick before. I don't
have time to try it out right now but I will in the next day or
so...just might do the trick!

-- Jeff
Closed Thread