473,386 Members | 1,773 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Inheritance and object factories...HELP!!

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
Jul 22 '05 #1
5 2084
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" <jg******@comcast.net> wrote in message
news:jg****************************@news.supernews .com...
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

Jul 22 '05 #2
* Jeff Greenberg:

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();
What you mean by "values" seems to be immutable objects.
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.


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?
Jul 22 '05 #3
Alf,

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

In article <41****************@news.individual.net>,
al***@start.no (Alf P. Steinbach) wrote:
> 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.


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>(); }
};


Unfortunately, this does not compile (at least on any compiler I have)
because
virtual Derived foo() const { return foo_<Derived>(); }
is an illegal override of
virtual Base foo() const { return foo_<Base>(); }
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?
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.
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?
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
}


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
Jul 22 '05 #4
* Jeff Greenberg:
Alf,

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

In article <41****************@news.individual.net>,
al***@start.no (Alf P. Steinbach) wrote:
>
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.


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>(); }
};


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


is an illegal override of
virtual Base foo() const { return foo_<Base>(); }


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?


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.

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.


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?


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.
}

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
}


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 :)


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?
Jul 22 '05 #5
In article <41****************@news.individual.net>,
al***@start.no (Alf P. Steinbach) wrote:
<......snip........>

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.


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
Jul 22 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: Gandu | last post by:
Could some C++ guru please help me? I have a very odd problem with respect templates and inheritance. I have templatized List class, from which I am inheriting to create a Stack class. All works...
1
by: Mark F. Haigh | last post by:
In Andrei Alexandrescu's book _Modern C++ Design_, Chapter 8 ("Object Factories"), Section 8.4 ("Type Identifiers"), he states: The only problem that remains is the management of type...
1
by: Patrick Stinson | last post by:
I am trying to create a way to register static members of a **class**, not an object, for making an object factory. The main thing I want is to be able to make a call like class MyClass {...
3
by: YUY0x7 | last post by:
I'm writing because I was having a little problem implementing one of the designs suggested in Modern C++ Design. The one I'm talking about is that of the class factory which registers classes...
6
by: Graham Pengelly | last post by:
Hi I'll try to spell out my problem as succinctly as possible... My database has a User table, an Organisation table, a Department table and a JobType table (amongst others) The...
4
by: Frawls | last post by:
Hi, I get the following error when trying to run a search on my aspx site, this error only occours if the product im searching for does not exist. Can anybody explain this please and help me...
0
by: Edwinah63 | last post by:
Hi guys, i am not new to vb6, but a mere tyro in vb.net what i want to do is the following open transaction build an ADODB command object using parameters execute it build another command...
3
by: BakelNB | last post by:
I am new to the .Net environment, so please bear with me. I am passing a form object (e.g. formA) to an existing form (e.g. formB) by setting a property value defined in formB to hold the formA...
6
by: Slain | last post by:
I have this question about what type of object is a memory block when you have inheritance going on Class A { blah blah blah }. Class B : public Class A {
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.