inheritance, list of objects, polymorphism

B

barbaros

Hello everybody,

I need help with an inheritance issue. I am implementing a sort of
symbolic calculus code. By the way, is the site www.ginac.de closed ?
Where can I find a mirror ?

Back to my code. I have a base class "Expression" and derived classes
"ExpressionSum", "ExpressionProduct", "ExpressionPower" and so on. For
instance, xy+yz+z is an ExpressionSum of two ExpressionProduct and an
Expression. The class ExpressionSum is implemented as a list of terms
to sum up. These terms should be any other Expressions (Products,
Powers, or mere Expressions). The problem is, when I call
ExpressionSum::add(expr), expr being an ExpressionProduct, it looks
like expr is first converted to a mere Expression and then added to
the list. This is different from what I want. I want a list of
Expressions, some of which should behave like Products, other like
Powers, etc.

I hope I made myself clear. I include below a small piece of code.
Thank you. Cristian Barbarosie http://cmaf.ptmat.fc.ul.pt/~barbaros/

#include <iostream>
#include <list>
using namespace std;

class Expression {
public:
string nname;
Expression ()
{ cout << "constructor Expression, no name provided" << endl;}
Expression (string n)
{ cout << "constructor Expression, name " << n << endl;
nname = n; }
virtual string name()
{ cout << "method Expression::name"<< endl; return nname; }
};

class ExpressionProduct : public Expression {
public:
list<Expression> factors;
ExpressionProduct ()
{cout << "constructor ExpressionProduct, no arguments" << endl;}
ExpressionProduct (const Expression &f)
{ cout << "constructor ExpressionProduct, one argument" << endl;
factors.push_back(f); }
ExpressionProduct (const Expression &f, const Expression &g)
{ cout << "constructor ExpressionProduct, two arguments" << endl;
factors.push_back(f);
factors.push_back(g); }
string name()
{ cout << "method ExpressionProduct::name"<< endl;
list<Expression>::iterator i=factors.begin();
if (i==factors.end()) return "1";
string n;
for (;i!=factors.end();i++) n = n + i->name();
return n; }
};

class ExpressionSum : public Expression {
public:
list<Expression> terms;
ExpressionSum ()
{cout << "constructor ExpressionSum, no arguments" << endl;}
void add (Expression &m)
{ cout << "adding" << endl; terms.push_back(m); }
string name()
{ cout << "method ExpressionSum::name"<< endl;
list<Expression>::iterator i=terms.begin();
if (i==terms.end()) return "0";
string n=i->name();
i++ ;
for (;i!=terms.end();i++)
n = n + '+' + i->name();
return n; }
};

int main () {
Expression x("x"), y("y"), z("z");
ExpressionProduct xy(x,y), yz(y,z);
cout << xy.name() << endl;
ExpressionSum p;
p.add(xy); p.add(yz); p.add(z);
cout << p.name() << endl;
}
 
M

Michael Doubez

I need help with an inheritance issue. I am implementing a sort of
symbolic calculus code. By the way, is the site www.ginac.de closed ?
No

Back to my code. I have a base class "Expression" and derived classes
"ExpressionSum", "ExpressionProduct", "ExpressionPower" and so on. For
instance, xy+yz+z is an ExpressionSum of two ExpressionProduct and an
Expression. The class ExpressionSum is implemented as a list of terms
to sum up. These terms should be any other Expressions (Products,
Powers, or mere Expressions). The problem is, when I call
ExpressionSum::add(expr), expr being an ExpressionProduct, it looks
like expr is first converted to a mere Expression and then added to
the list.

Yes, you stores Expression values.
You should store Expression pointers which raises the problem of deep
copy. It is usually solved by implementing a 'virtual Expression* clone
()' operation.

Another alternative is to use the Envelope/Letter idiom.
This is different from what I want. I want a list of
Expressions, some of which should behave like Products, other like
Powers, etc.

I hope I made myself clear.
Yes

I include below a small piece of code. [snip]
class Expression { [snip]
class ExpressionProduct : public Expression { [snip]
class ExpressionSum : public Expression {
public:
  list<Expression> terms;

Here you have slicing of your objects.
If you make Expression virtual pure, you won't be able to compile your
code.
 
J

John H.

Yes, you stores Expression values.

I agree that this is the problem. Your lists are of type Expression,
so only Expression objects will live in the list. When you push_back
onto the list, Expression objects are created. Since an Expression
object doesn't have the members "factors" or "terms", you lose this
information when the Expression object is created (from the
ExpressionSum or ExpressionProduct object).

One thing you might try is to make your lists hold pointers to
Expression objects. This way the list can work with Expression
objects or one of it's derived types (ExpressionSum or
ExpressionProduct). Keep in mind, that since you are dealing with a
list of pointers, you have to make sure that the objects pointed to
continue to exist throughout the life of the list.
Here is an example with pointers, although smart pointers would be a
better idea. (Also, I put in some more additional const correctness.)

#include <iostream>
#include <list>
using namespace std;

class Expression {
public:
string nname;
Expression ()
{ cout << "constructor Expression, no name provided" << endl;}
Expression (string n)
{ cout << "constructor Expression, name " << n << endl;
nname = n; }
virtual string name() const
{ cout << "method Expression::name"<< endl; return nname; }

};

class ExpressionProduct : public Expression {
public:
list<const Expression *> factors;
ExpressionProduct ()
{cout << "constructor ExpressionProduct, no arguments" << endl;}
ExpressionProduct (const Expression &f)
{ cout << "constructor ExpressionProduct, one argument" << endl;
factors.push_back(&f); }
ExpressionProduct (const Expression &f, const Expression &g)
{ cout << "constructor ExpressionProduct, two arguments" << endl;
factors.push_back(&f);
factors.push_back(&g); }
string name() const
{ cout << "method ExpressionProduct::name"<< endl;
list<const Expression *>::const_iterator i=factors.begin();
if (i==factors.end()) return "1";
string n;
for (;i!=factors.end();i++) n = n + (*i)->name();
return n; }

};

class ExpressionSum : public Expression {
public:
list<const Expression *> terms;
ExpressionSum ()
{cout << "constructor ExpressionSum, no arguments" << endl;}
void add (const Expression &m)
{ cout << "adding" << endl; terms.push_back(&m); }
string name() const
{ cout << "method ExpressionSum::name"<< endl;
list<const Expression *>::const_iterator i=terms.begin();
if (i==terms.end()) return "0";
string n=(*i)->name();
i++ ;
for (;i!=terms.end();i++)
n = n + '+' + (*i)->name();
return n; }

};

int main () {
Expression x("x"), y("y"), z("z");
ExpressionProduct xy(x,y), yz(y,z);
cout << xy.name() << endl;
ExpressionSum p;
p.add(xy); p.add(yz); p.add(z);
cout << p.name() << endl;
return 0;
}
 
J

James Kanze

Yes, you stores Expression values. You should store Expression
pointers which raises the problem of deep copy. It is usually solved
by implementing a 'virtual Expression* clone ()' operation.
Another alternative is to use the Envelope/Letter idiom.

Which just hides the cloning. If Expression objects are immutable
(and
they probably should be), then a much simpler solution would be to use
reference semantics (i.e. pointers) everywhere. Depending on the
application, this might even be a case where shared_ptr is
appropriate---an expression tree is just that, a tree, and can't have
cycles. (There are other solutions, with better performance, but they
generally require more work.)
[snip]
class Expression { [snip]
class ExpressionProduct : public Expression { [snip]
class ExpressionSum : public Expression {
public:
list<Expression> terms;
Here you have slicing of your objects.
If you make Expression virtual pure, you won't be able to compile your
code.

General rule: assignment and external copy don't work well with
inheritance. (In my own code, I've gradually been introducing a
PolymorphicObject base class, with a virtual destructor and a private
copy constructor and assignment operator. With the rule that classes
designed to be used polymorphically should inherit from
PolymorphicObject.)
 
V

Vladimir Jovic

James said:
> General rule: assignment and external copy don't work well with
inheritance. (In my own code, I've gradually been introducing a
PolymorphicObject base class, with a virtual destructor and a private
copy constructor and assignment operator. With the rule that classes
designed to be used polymorphically should inherit from
PolymorphicObject.)

I do not understand why you said that "assignment and external copy
don't work well with inheritance."

Can you provide a simple example how your code looks like using
PolymorphicObject?
 
A

Alf P. Steinbach

* Vladimir Jovic:
I do not understand why you said that "assignment and external copy
don't work well with inheritance."

Mainly it has to do with C++ variables directly being of the size of the
statically known type and directly containing an object of that type, instead of
just being pointers[1] as in Java and C# and like languages.

When sizeof(Derived) > sizeof(Base) this means that

Base o = Derived();

performs a /slice/ of the Derived object; 'o' contains only the Base stuff of
that original object.

Additionally, the copy is now a Base, so any overriding of functionality in
Derived is lost.

Even worse, consider

Derived o;
Base& b = o;
b = Base();

Perhaps Base has a person's name and Derived additional has the person's
birth-year, then the above changes the 'o' name without updating the birth-year,
yielding a Derived instance with inconsistent information.

For a PolymorphicObject base class like James mentioned you therefore generally
want to introduce two restrictions, and support one main functionality:

* Inhibit client code "slice" copying.
This is done by making the assignment operator private and the
copy constructor protected. James wrote "private" copy constructor
but that's a bit impractical. For you want to allow derived classes
to be clonable, and cloning is best expressed in terms of internal
copy construction.

* Make sure that objects can only be created dynamically.
The reasonable way is to make the destructor protected.

* Force use of smart pointer.
James relies on garbage collection so he probably doesn't do this,
but there are two aspects: ensuring that any newly created object's
raw pointer is immediately stored in a smart pointer, and ensuring
that only the smart pointer class has access to destroy an object.
One way to do the first it is to overload the class' allocation function
(operator new) so that any direct 'new' expression would be overly
complicated. For C++98 then provide a macro that supplies the
requisite magic incomprehensible expression and ensures the pointer
is immediately wrapped in a smart pointer, before client code can
get at it. For C++0x I think the improved support for argument
forwarding makes the macro unnecessary. Anyways, one way to do the
second is to make destructor protected (which you'd do anyway for
the bullet point above), and grant friendship to the smart pointer.

Cheers & hth.,

- Alf


Notes:
[1] Even though Java /programmers/ often think that Java doesn't have pointers,
the Java language specification uses that (correct) terminology. C++ programmers
are more conscious of the low level and formal stuff, because they have to be:
C++ is much more complicated... So, I'm not confusing terms here.
 
M

Michael Doubez

Which just hides the cloning.

And handles automatically the deletion in containers and some hard
cases of exception safety.

I also though the Envelope/Letter idiom would be a good choice for
symbolic calculus code because type promotion is handled seamlessly.
 If Expression objects are immutable
(and
they probably should be), then a much simpler solution would be to use
reference semantics (i.e. pointers) everywhere.  Depending on the
application, this might even be a case where shared_ptr is
appropriate---an expression tree is just that, a tree, and can't have
cycles.  (There are other solutions, with better performance, but they
generally require more work.)


Yes. Or something like boost's intrusive_ptr<>; AMA it wouldn't be too
onerous in development.

[snip]
 
B

barbaros

First, I wish to thank everyone for their support.

[...]  Your lists are of type Expression,
so only Expression objects will live in the list.  When you push_back
onto the list, Expression objects are created.

Could you please elaborate a little bit on that ?
I am confused because I have output instructions in each and every
constructor. So, if new Expression objects were created, I should
obtain messages on the screen before or after each "add" operation.
I include below the output of the code in my first message
(under Debian 4.3.3-3)

constructor Expression, name x
constructor Expression, name y
constructor Expression, name z
constructor Expression, no name provided
constructor ExpressionProduct, two arguments
constructor Expression, no name provided
constructor ExpressionProduct, two arguments
method ExpressionProduct::name
method Expression::name
method Expression::name
xy
constructor Expression, no name provided
constructor ExpressionSum, no arguments
adding
adding
adding
method ExpressionSum::name
method Expression::name
method Expression::name
method Expression::name
++z
 
M

Michael Doubez

First, I wish to thank everyone for their support.

[...]  Your lists are of type Expression,
so only Expression objects will live in the list.  When you push_back
onto the list, Expression objects are created.

Could you please elaborate a little bit on that ?
I am confused because I have output instructions in each and every
constructor. So, if new Expression objects were created, I should
obtain messages on the screen before or after each "add" operation.
I include below the output of the code in my first message
(under Debian 4.3.3-3)

constructor Expression, name x
constructor Expression, name y
constructor Expression, name z
constructor Expression, no name provided
constructor ExpressionProduct, two arguments
constructor Expression, no name provided
constructor ExpressionProduct, two arguments
method ExpressionProduct::name
method Expression::name
method Expression::name
xy
constructor Expression, no name provided
constructor ExpressionSum, no arguments
adding
adding
adding
method ExpressionSum::name

You can have a problem hereafter.
method Expression::name
method Expression::name
method Expression::name

If your code was correct, the output should have been:
method ExpressionProduct::name
method ExpressionProduct::name
method Expression::name

It is because your container stores only the Expression part of
ExpressionProduct.
 
V

Vladimir Jovic

Alf said:
* Vladimir Jovic:
I do not understand why you said that "assignment and external copy
don't work well with inheritance."

Mainly it has to do with C++ variables directly being of the size of the
statically known type and directly containing an object of that type,
instead of just being pointers[1] as in Java and C# and like languages.

When sizeof(Derived) > sizeof(Base) this means that

Base o = Derived();

performs a /slice/ of the Derived object; 'o' contains only the Base
stuff of that original object.

Ok, this explains it :)

[snip]
For a PolymorphicObject base class like James mentioned you therefore
generally want to introduce two restrictions, and support one main
functionality:

* Inhibit client code "slice" copying.
This is done by making the assignment operator private and the
copy constructor protected. James wrote "private" copy constructor
but that's a bit impractical. For you want to allow derived classes
to be clonable, and cloning is best expressed in terms of internal
copy construction.

The assignment operator is private and not implemented, or just private?
* Make sure that objects can only be created dynamically.
The reasonable way is to make the destructor protected.

But if you make the constructor protected, and leave the destructor
public, then you do not need next point. Or, am I missing something?
 
J

James Kanze

James Kanze wrote:
I do not understand why you said that "assignment and external copy
don't work well with inheritance."

In the case of polymorphic objects, the "value" of an object includes
its type, and you cannot change the type of a declared object. Given
something like:

Base* p1 = new Derived1;
Base* p2 = new Derived2;
*p1 = *p2;

What should this mean? The same thing occurs with external copy: you
have to specify the name of the constructed type, something like:

Base o( *p1 );

This constructs a Base, not a Derived1.
Can you provide a simple example how your code looks like using
PolymorphicObject?

class Expression : public PolymorphicObject
{
// ...
};

That's all that's necessary.
 
A

Alf P. Steinbach

* Vladimir Jovic:
Alf said:
* Vladimir Jovic:
James Kanze wrote:
General rule: assignment and external copy don't work well with
inheritance. (In my own code, I've gradually been introducing a
PolymorphicObject base class, with a virtual destructor and a private
copy constructor and assignment operator. With the rule that classes
designed to be used polymorphically should inherit from
PolymorphicObject.)

I do not understand why you said that "assignment and external copy
don't work well with inheritance."

Mainly it has to do with C++ variables directly being of the size of
the statically known type and directly containing an object of that
type, instead of just being pointers[1] as in Java and C# and like
languages.

When sizeof(Derived) > sizeof(Base) this means that

Base o = Derived();

performs a /slice/ of the Derived object; 'o' contains only the Base
stuff of that original object.

Ok, this explains it :)

[snip]
For a PolymorphicObject base class like James mentioned you therefore
generally want to introduce two restrictions, and support one main
functionality:

* Inhibit client code "slice" copying.
This is done by making the assignment operator private and the
copy constructor protected. James wrote "private" copy constructor
but that's a bit impractical. For you want to allow derived classes
to be clonable, and cloning is best expressed in terms of internal
copy construction.

The assignment operator is private and not implemented, or just private?

There's usually no need to implement it.

I can't imagine why one need it, but it's always like, someone's going to need
that anyway.

So the answer is "it depends", but in general, no implementation.

But if you make the constructor protected, and leave the destructor
public, then you do not need next point. Or, am I missing something?

Yeah. First of all you need the destructor protected anyway, to stop the
long-fingered client code programmer from doing 'delete &*smart_ptr'. Or more
indirectly. And secondly, with M classes with on average N constructors each
then for protected constructors you need M*N factory functions, while with
destructor protected you need, uh, none. Or just a single common one, if
wrapping of new expression is regarded as a factory function. :)


Cheers & hth.,

- Alf
 
D

Dilip

Hello everybody,

I need help with an inheritance issue. I am implementing a sort of
symbolic calculus code. By the way, is the sitewww.ginac.declosed ?
Where can I find a mirror ?

Back to my code. I have a base class "Expression" and derived classes
"ExpressionSum", "ExpressionProduct", "ExpressionPower" and so on. For
instance, xy+yz+z is an ExpressionSum of two ExpressionProduct and an
Expression. The class ExpressionSum is implemented as a list of terms
to sum up. These terms should be any other Expressions (Products,
Powers, or mere Expressions). The problem is, when I call
ExpressionSum::add(expr), expr being an ExpressionProduct, it looks
like expr is first converted to a mere Expression and then added to
the list. This is different from what I want. I want a list of
Expressions, some of which should behave like Products, other like
Powers, etc.

Your explanation seems eerily similar to the concept of Expression
Trees as a part of the LINQ infrastructure in C#. I know its slightly
off topic but you can look at the System.Linq.Expressions hierarchy
and get an idea.
http://msdn.microsoft.com/en-us/library/bb397951.aspx
http://msdn.microsoft.com/en-us/library/system.linq.expressions.aspx
 
J

James Kanze

* Vladimir Jovic:
Mainly it has to do with C++ variables directly being of the size of
the statically known type and directly containing an object of that
type, instead of just being pointers[1] as in Java and C# and like
languages.
When sizeof(Derived) > sizeof(Base) this means that
Base o = Derived();
performs a /slice/ of the Derived object; 'o' contains only the Base
stuff of that original object.

Not just when the sizes are different. The fact that the derived type
can be bigger than the base type (and that the compiler needs to know
the size static and member variables) may be the motivation here, but
the important point is that an object in C++ (or in Java) cannot
change
its type, and that variables in C++ do have object type (rather than
reference type, as in Java). And slicing occurs even if the sizes are
the same---o has type Base.

In many cases (and almost certainly in his), the base class should be
abstract, which guarantees no slicing (since you can't have instances
of
an abstract type).
Additionally, the copy is now a Base, so any overriding of
functionality in Derived is lost.
Even worse, consider
Derived o;
Base& b = o;
b = Base();
Perhaps Base has a person's name and Derived additional has the
person's birth-year, then the above changes the 'o' name without
updating the birth-year, yielding a Derived instance with inconsistent
information.
For a PolymorphicObject base class like James mentioned you therefore
generally want to introduce two restrictions, and support one main
functionality:
* Inhibit client code "slice" copying.
This is done by making the assignment operator private and the
copy constructor protected. James wrote "private" copy constructor
but that's a bit impractical. For you want to allow derived classes
to be clonable, and cloning is best expressed in terms of internal
copy construction.

Oops. You're right, if you want to support cloning, *and* the base
class has state, you'll need a protected copy constructor. (Again, in
the most common scenario, and the one that should be used here, the
base
class will be an "interface": abstract and without state. And the
derived classes copy constructors can simply ignore it.)
* Make sure that objects can only be created dynamically.
The reasonable way is to make the destructor protected.

In practice, I suspect that this may be overkill if the base class is
abstract. Except for construction, client code will only use the base
class. And there's no way they can accidentally declare a variable
with
the type of the base class.

Also, in the very special case of Expression (and maybe one or two
others), there are scenarios where you don't want dynamic allocation
(despite polymorphism). The mode today is to use templates for
compile
time expression evaluation, but in the past, a virtual hierarchy based
on expression did the trick just as well---provided all of the
instances
of the derived class were temporaries on the stack, so that the
compiler
knew the actual types and could inline the virtual functions. (But as
I
said, that's a very special case.)
* Force use of smart pointer.
James relies on garbage collection so he probably doesn't do
this, but there are two aspects: ensuring that any newly created
object's raw pointer is immediately stored in a smart pointer,
and ensuring that only the smart pointer class has access to
destroy an object.

It depends on context, and I don't use garbage collection everywhere.
(Only when I can.) And as I mentionned somewhere, in this particular
case, boost::shared_ptr is a more than adequate solution. Probably
slower than the obvious alternatives (including garbage collection),
but
probably fast enough, and certainly a lot simpler to implement, unless
you're already using garbage collection.

For this case. In many other cases (entity objects, etc.), you don't
really want smart pointers except temporarily (if then---often,
everything that's necessary to accomplish before the smart pointer
gives
up ownership can be done in the constructor).
One way to do the first it is to overload the
class' allocation function (operator new) so that any direct
'new' expression would be overly complicated. For C++98 then
provide a macro that supplies the requisite magic
incomprehensible expression and ensures the pointer is
immediately wrapped in a smart pointer, before client code can
get at it.

You don't need to be that complicated. Just make the constructors
private, and provide a factory function which returns a smart pointer.
Something like:

class Expression
{
Expression( Expression const& );
Expression& operator=(Expression const& );
protected:
Expression() {}
~Expression() {}
public:
typedef boost::shared_ptr< Expression > Ptr;

virtual double value() const = 0;
};

class AddExpression : public Expression
{
Ptr lhs;
Ptr rhs;

AddExpression( Ptr lhs, Ptr rhs )
: m_lhs( lhs )
, m_rhs( rhs )
{
}

public:
static Ptr create( Ptr lhs, Ptr rhs )
{
return Ptr( new AddExpression( lhs, rhs ) );
}
virtual double value() const
{
return lhs->value() + rhs->value();
}
};

That should go a long way to offering the protection you want.
 
J

James Kanze

On 16 déc, 09:53, James Kanze <[email protected]> wrote:

[...]
And handles automatically the deletion in containers and some hard
cases of exception safety.
I also though the Envelope/Letter idiom would be a good choice for
symbolic calculus code because type promotion is handled seamlessly.

If you need to support promotion, yes. Envelope/Letter is the way to
go. (That is, after all, what Coplien invented it for.) But for just
simple expressions, where the objects represent nodes in the
expression
tree, it's probably overkill; just using pointers (shared_ptr if you
don't have anything better for memory management) is largely
sufficient,
and considerably easier to implement.
Yes. Or something like boost's intrusive_ptr<>; AMA it wouldn't be too
onerous in development.

Or installing garbage collection. But installation of the Boehm
collector isn't always trivial. But what I was thinking of was some
sort of block allocator, where all of the nodes in a given expression
(and no other nodes) are allocated by a separate instance of the
allocator, and when finished with the entire expression, you simply
destruct the allocator, and that frees all of the memory it manages.
Without the need to walk the tree deleting the nodes one by one.
(Done
correctly, allocating a single node is just a comparison and a
subtraction -- an order of magnitude faster than more general
allocators --, and you never free an individual node.)
 
A

Alf P. Steinbach

* James Kanze:
* Vladimir Jovic:
Mainly it has to do with C++ variables directly being of the size of
the statically known type and directly containing an object of that
type, instead of just being pointers[1] as in Java and C# and like
languages.
When sizeof(Derived) > sizeof(Base) this means that
Base o = Derived();
performs a /slice/ of the Derived object; 'o' contains only the Base
stuff of that original object.

Not just when the sizes are different. The fact that the derived type
can be bigger than the base type (and that the compiler needs to know
the size static and member variables) may be the motivation here, but
the important point is that an object in C++ (or in Java) cannot
change
its type, and that variables in C++ do have object type (rather than
reference type, as in Java). And slicing occurs even if the sizes are
the same---o has type Base.

I believe the point you raise was addressed in the immediately following paragraph:
In many cases (and almost certainly in his), the base class should be
abstract, which guarantees no slicing (since you can't have instances
of
an abstract type).

I wouldn't rely on such a guarantee.

More to the point, you don't yourself rely on such a guarantee. :)

Because nothing stops anyone from deriving a concrete class with further dervied
classes.


Oops. You're right, if you want to support cloning, *and* the base
class has state, you'll need a protected copy constructor. (Again, in
the most common scenario, and the one that should be used here, the
base
class will be an "interface": abstract and without state. And the
derived classes copy constructors can simply ignore it.)


In practice, I suspect that this may be overkill if the base class is
abstract. Except for construction, client code will only use the base
class. And there's no way they can accidentally declare a variable
with
the type of the base class.

I can think of many ways that someone inadvertently declares an automatic
variable of some concrete derived class.

I think it's better to just design that possible bug vector away.

It's the distinction that you often make in this group between objects with
identity and those with just value, where the former are best designed so that
they can only be used with dynamic allocation.

Also, in the very special case of Expression (and maybe one or two
others), there are scenarios where you don't want dynamic allocation
(despite polymorphism). The mode today is to use templates for
compile
time expression evaluation, but in the past, a virtual hierarchy based
on expression did the trick just as well---provided all of the
instances
of the derived class were temporaries on the stack, so that the
compiler
knew the actual types and could inline the virtual functions. (But as
I
said, that's a very special case.)


It depends on context, and I don't use garbage collection everywhere.
(Only when I can.) And as I mentionned somewhere, in this particular
case, boost::shared_ptr is a more than adequate solution. Probably
slower than the obvious alternatives (including garbage collection),
but
probably fast enough, and certainly a lot simpler to implement, unless
you're already using garbage collection.

For this case. In many other cases (entity objects, etc.), you don't
really want smart pointers except temporarily (if then---often,
everything that's necessary to accomplish before the smart pointer
gives
up ownership can be done in the constructor).


You don't need to be that complicated. Just make the constructors
private, and provide a factory function which returns a smart pointer.
Something like:

class Expression
{
Expression( Expression const& );
Expression& operator=(Expression const& );
protected:
Expression() {}
~Expression() {}
public:
typedef boost::shared_ptr< Expression > Ptr;

virtual double value() const = 0;
};

class AddExpression : public Expression
{
Ptr lhs;
Ptr rhs;

AddExpression( Ptr lhs, Ptr rhs )
: m_lhs( lhs )
, m_rhs( rhs )
{
}

public:
static Ptr create( Ptr lhs, Ptr rhs )
{
return Ptr( new AddExpression( lhs, rhs ) );
}
virtual double value() const
{
return lhs->value() + rhs->value();
}
};

That should go a long way to offering the protection you want.

That's a per class solution. And it requires one factory function per
constructor. That's sort of ugly, not to mention laborious, and since you're
designing a common PolymorphicObject base class I think you may save a lot of
work by centralizing the functionality there, -- even though it relies on a
convention, that all derived classes also declare destructors protected (it's a
shame that the accessibility can't be inherited automatically!).


Cheers,

- Alf
 
B

barbaros

M

Michael Doubez

Your remark is very interesting, thank you for that.
It's a pitty this has not been done for C++.
Cristian Barbarosiehttp://cmaf.ptmat.fc.ul.pt/~barbaros/

Boost's Spirit/Phoenix can get you a long way to do the same but it is
not for the faint of heart :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top