Hmm... inheritence... hmmm

J

JKop

Been thinking about the following:

class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

But... then you have

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &)
{
//Wait a minute, I'm not mating unless
//it's with another dog!
}
};


Has anyone met this situation before? Obviously my first thought was:


virtual void Mate(Dog &);


but then that doesn't overload the base class function...


I know the following would work, but it seems a bit inefficent to me:


#include <typeinfo>

virtual void Mate(Mammal &mammal)
{
//Wait a minute, I'm not mating unless
//it's with another dog!

try
{
Dog& doggy = dynamic_cast<Dog&>(mammal);

//Now perform mating
}
catch( std::bad_cast )
{

}
}


Any thoughts on this?


-JKop
 
M

Michael Kurz

JKop said:
Been thinking about the following:

class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

But... then you have

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &)
{
//Wait a minute, I'm not mating unless
//it's with another dog!
}
};


Has anyone met this situation before? Obviously my first thought was:


virtual void Mate(Dog &);


but then that doesn't overload the base class function...


I know the following would work, but it seems a bit inefficent to me:


#include <typeinfo>

virtual void Mate(Mammal &mammal)
{
//Wait a minute, I'm not mating unless
//it's with another dog!

try
{
Dog& doggy = dynamic_cast<Dog&>(mammal);

//Now perform mating
}
catch( std::bad_cast )
{

}
}


Any thoughts on this?

Actual IMHO "mate", should not be a member function at all, as the order
seems not important within your model.
(Quite similar to operator +, ...)

And the problem in general is a "multi method problem", which can not be
solved by C++ language constructs directly as C++ does not have built in
multi methods. There is a nice chapter within "Modern C++ Design" from
Andrei Alexandrescu how to implement Multimethods.

This is often implemented with a function lookup table, where you can simply
"lookup" the mate operaion given a pair of operands.
(assumed you need the runtime support, as your "virtual" suggested, a
compiletime solution can of course be implemented much easier with template
specialisation, where the not specialized template causes an compiler error,
which means that the requested mate is not implemented==not supported)



Regards
Michael
 
D

Daniel T.

JKop said:
Been thinking about the following:

class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

You are wrong. For the above to be rational, every Mammal must be able
to mate with every other Mammal; which is not the case.

But... then you have

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &)
{
//Wait a minute, I'm not mating unless
//it's with another dog!
}
};


Has anyone met this situation before?

This is the same as the classic rectangle/square problem. Even the above
doesn't work...

void fun( Dog& boy, Dog& boy ) {
boy.mate( boy );
}

....
Any thoughts on this?

You are trying to generalize where no generalization is possible.
 
I

Ioannis Vranos

JKop said:
Been thinking about the following:

class Mammal


And immediately "Teach Yourself C++ in 21 Days" by Jesse Liberty comes
to my mind.

This was my first C++ book. :) If you read it, keep reading it.

{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

But... then you have

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &)
{
//Wait a minute, I'm not mating unless
//it's with another dog!
}
};


Has anyone met this situation before? Obviously my first thought was:


virtual void Mate(Dog &);


but then that doesn't overload the base class function...


I know the following would work, but it seems a bit inefficent to me:


#include <typeinfo>

virtual void Mate(Mammal &mammal)
{
//Wait a minute, I'm not mating unless
//it's with another dog!

try
{
Dog& doggy = dynamic_cast<Dog&>(mammal);

//Now perform mating
}
catch( std::bad_cast )
{

}
}


Any thoughts on this?



class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};

class Dog : public Mammal
{
public:

Dog(Mammal &) {}
Dog(){}

virtual void Mate(Mammal &x)
{
Dog d = x;

// ...
}
};
 
I

Ioannis Vranos

Ioannis said:
class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};

class Dog : public Mammal
{
public:

Dog(Mammal &) {}
Dog(){}

virtual void Mate(Mammal &x)
{
Dog d = x;

// ...
}
};


This of course if you want your dog objects to consider every mamal as a
dog in this specific functionality. :)
 
J

JKop

Daniel T. posted:
You are wrong. For the above to be rational, every Mammal must be able
to mate with every other Mammal; which is not the case.

I understand your rationale, but at the same time, if I were to omit the
function "Mate", then it would be possible to have a Mammal that doesn't
mate...

Something just came to mind actually:

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &) {}

virtual void Mate(Dog &dog)
{
//Now it's safe to mate.
}
};


-JKop
 
R

Rolf Magnus

Ioannis said:
This of course if you want your dog objects to consider every mamal as a
dog in this specific functionality. :)

Maybe the class should then be called "BlindDog"? :)
 
J

JKop

Ioannis Vranos posted:
And immediately "Teach Yourself C++ in 21 Days" by Jesse Liberty comes
to my mind.

This was my first C++ book. :) If you read it, keep reading it.


Nope, I just consider the whole mammal hierarchy concept to be a good, easy
example.


class Organism {};

class Carbon_Based_Organism : public Organism {};

class Creature : public Carbon_Based_Organsim {};

class Mammal : public Creature {};

class Reptile : public Creature {};

class DuckBilledPlatypus : public Mammal, public Reptile {}; //There should
be virtual inheritence there, but I'd have to look up the syntax...

class Dog : public Mammal {};


See how easy it is to work with the concept of "Mammal"! You can expand on
it in every direction quite easily!


-JKop
 
I

Ioannis Vranos

JKop said:
I understand your rationale, but at the same time, if I were to omit the
function "Mate", then it would be possible to have a Mammal that doesn't
mate...

Something just came to mind actually:

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &) {}

virtual void Mate(Dog &dog)
{
//Now it's safe to mate.
}
};



Mate() should not be virtual. It doesn't make sense. Leave it non
virtual and think about it.
 
R

Rolf Magnus

JKop said:
Daniel T. posted:


I understand your rationale, but at the same time, if I were to omit the
function "Mate", then it would be possible to have a Mammal that doesn't
mate...

Something just came to mind actually:

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &) {}

virtual void Mate(Dog &dog)
{
//Now it's safe to mate.
}
};

I don't see what you gain from that. Do you want to add an empty Mate
function to every class derived from mammal?
Further, the idea of a virtual function is that you can use it though a
pointer or reference to the base class and still have the correct derived
class function called. That's not the case either with your example, since
the first mate function doesn't do anything, and the second one is not (or
should not be) part of the base class's interface and thus will never be
called if your object is used though that interface.
 
A

Alf P. Steinbach

* JKop:
Been thinking about the following:

class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

One little step:


class FeMammal: public Mammal
{
public:
virtual std::vector<Mammal> procreateWith( HeMammal& x )
{
mateWith( x );
changeFoodHabitsRadically();
return offspring();
}
};


Now it starts to get both more interesting and political... ;-)

The interesting stuff programminglanguagewise: both the function result
and the argument x should be covariant, and every Mammal-derived class should
automatically have a Fe... and a He... derived class with given properties,
except the Fe... and He... classes. Hm, how to express this in C++...
 
H

Howard

JKop said:
Daniel T. posted:


I understand your rationale, but at the same time, if I were to omit the
function "Mate", then it would be possible to have a Mammal that doesn't
mate...

Something just came to mind actually:

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &) {}

virtual void Mate(Dog &dog)
{
//Now it's safe to mate.
}
};

OT....

You forgot:


void Mate( Pillow& pillow );
void Mate( Couch& couch );
void Mate( Leg& leg );
void Mate( HoleInLog& holeinlog );
....etc....

At least, that's my experience with *male* dogs! :)
 
M

Michael Lehn

JKop said:
Been thinking about the following:

class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

But... then you have

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &)
{
//Wait a minute, I'm not mating unless
//it's with another dog!
}
};


Has anyone met this situation before? Obviously my first thought was:


virtual void Mate(Dog &);


but then that doesn't overload the base class function...


I know the following would work, but it seems a bit inefficent to me:


#include <typeinfo>

virtual void Mate(Mammal &mammal)
{
//Wait a minute, I'm not mating unless
//it's with another dog!

try
{
Dog& doggy = dynamic_cast<Dog&>(mammal);

//Now perform mating
}
catch( std::bad_cast )
{

}
}


Any thoughts on this?


-JKop

what about the POOMA engine approach:


template <typename Genus>
class Mammal
{
public:
Genus &
genus()
{
return genus;
}

template <typename AnotherGenus>
void
mate(const Mammal<AnotherGenus> &m)
{
mate(*this, m); // do it (if possible)
}


private:
Genus genus;
};

class Dog
{
};

// let dogs mate
void
mate(Mammal<Dog> &d1, const Mammal<Dog> &d2)
{
// no details
}

class Cat
{
};

// let cats mate
void
mate(Mammal<Cat> &d1, const Mammal<Cat> &d2)
{
// no details
}

int
main()
{
Mammal<Dog> fix, foxi;
Mammal<Cat> tom;

fix.mate(foxi);
fix.mate(tom); // outch!
}


who can mate with whom depends on whether you write a mate-function for the
combination or not.
 
J

JKop

OT....

You forgot:


void Mate( Pillow& pillow );
void Mate( Couch& couch );
void Mate( Leg& leg );
void Mate( HoleInLog& holeinlog );
...etc....

At least, that's my experience with *male* dogs! :)


Well maybe if you kept the odd female dog around, it wouldn't have to!

My dog's neutered; he gets some pretty females coming up to him looking for
attention, but he just brushes past. If only he knew what he was missing...
;-D


-JKop
 
M

Mike Wahler

Rolf Magnus said:
Maybe the class should then be called "BlindDog"? :)

<OT>
I have a rabbit (who roams free in my yard) who constantly
attempts to 'do the deed' to my cat. Puzzling, but very
amusing (except to the cat). Well, he *is* a rabbit,
after all. With an apparently 'virtual' libido. :)
</OT>

-Mike
 
G

Gianni Mariani

JKop said:
Been thinking about the following:

class Mammal
{
public:

virtual void Mate(Mammal &) = 0;
};


The above seems rational to me - if something is to qualify as a Mammal,
then it must be able to mate.

But... then you have

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &)
{
//Wait a minute, I'm not mating unless
//it's with another dog!
}
};


Has anyone met this situation before? Obviously my first thought was:


virtual void Mate(Dog &);


but then that doesn't overload the base class function...


I know the following would work, but it seems a bit inefficent to me:


#include <typeinfo>

virtual void Mate(Mammal &mammal)
{
//Wait a minute, I'm not mating unless
//it's with another dog!

try
{
Dog& doggy = dynamic_cast<Dog&>(mammal);

//Now perform mating
}
catch( std::bad_cast )
{

}
}


Any thoughts on this?

I use this pattern :

class Mammal;
class Dog;
class Cat;
class Snake;

class Mater
{
public:

virtual void Ouch(Mammal &) = 0;

virtual void TryMate(Dog & i_mamal);
virtual void TryMate(Cat & i_mamal);
virtual void TryMate(Snake & i_mamal);

};


class Mammal : public Mater
{
public:

virtual void Mate(Mammal &) = 0;
};


class Dog
: public Mammal
{
public:

virtual void Mate(Mammal & i_mamal)
{
i_mamal.TryMate( * this );
}

virtual void Ouch(Mammal &)
{
// don't - too hard
}

virtual void TryMate(Dog & i_dog)
{
if ( i_dog.is_cute() )
{
//...
}
}

bool is_cute();

};

//
// just replicate Dog

class Cat
: public Mammal
{
// ...
};

class Snake
: public Mammal
{
// ...
};

//
// by default, all attempts to make are Ouch...

void Mater::TryMate(Dog & i_mamal)
{
Ouch( i_mamal );
}

void Mater::TryMate(Cat & i_mamal)
{
Ouch( i_mamal );
}

void Mater::TryMate(Snake & i_mamal)
{
Ouch( i_mamal );
}

The problem with this is that you at least need to know at compile time
all the possible types of mamal.

One other possibility is to use a generic factory registry to find a
function that can mate.


class Mater
{
virtual void Mate( Mammal & i_mamal1, Mammal & i_mamal2 ) = 0;
};


class Mammal
{
public:

virtual void Mate(Mammal &) = 0;

virtual typeid MyType() = 0;
};


class Dog
: public Mammal
{
public:

virtual typeid Type()
{
return typeof( *this );
}

virtual void Mate(Mammal & i_mamal)
{
Mater * mater = FindMater( pair( Type(), i_mamal.Type() ) );

if ( ! mater )
{
// OUCH
return;
}

mater->Mate( *this, i_mamal );
}

bool is_cute();

};



// hidden in a different compile unit
class DogMater : public Mater
{
virtual void Mate( Mammal & i_mamal1, Mammal & i_mamal2 )
{
Dog & l_dog1 = static_cast<Dog &>( i_mamal1 );
Dog & l_dog2 = static_cast<Dog &>( i_mamal2 );

// do stuff with dogs ...
}
};

REGISTER( pair( typeof( Dog ), typeof( Dog ) ), DogMater, Mater );
 
A

Adrian

JKop said:
Been thinking about the following:
How about?

#include <iostream>
#include <typeinfo>

class Mammal
{
public:
virtual void Mate(Mammal &m) = 0;
};

class Dog : public Mammal
{
public:
virtual void Mate(Mammal &m)
{
if(typeid(*this)==typeid(m))
{
std::cout << "ok" << std::endl;
}
else
{
std::cout << "no chance your not my type" << std::endl;
}
};
};

class Cat : public Mammal
{
public:
virtual void Mate(Mammal &m)
{
if(typeid(*this)==typeid(m))
{
std::cout << "ok" << std::endl;
}
else
{
std::cout << "no chance your not my type" << std::endl;
}
};
};

int main()
{
Cat c1,c2;
Dog d1,d2;
Mammal *ptr;

ptr=&c2;
c1.Mate(*ptr);
ptr=&d2;
c1.Mate(*ptr);

ptr=&d2;
d1.Mate(*ptr);
ptr=&c2;
d1.Mate(*ptr);

return 0;
}
 
D

Daniel T.

JKop said:
Daniel T. posted:


I understand your rationale, but at the same time, if I were to omit the
function "Mate", then it would be possible to have a Mammal that doesn't
mate...

Absolutely not. If client code needs a particular object to mate, then
the class from which that object comes must have a 'mate'
member-function.

Your problem doesn't exist because no piece of code needs to be able to
take two "mammals" and force them to mate. I.E.

void func( Mammal& a, Mammal& b ) {
a.mate( b );
}

The above doesn't exist anywhere in the code base.

Something just came to mind actually:

class Dog : public Mammal
{
public:

virtual void Mate(Mammal &) {}

virtual void Mate(Dog &dog)
{
//Now it's safe to mate.
}
};

This doesn't solve your stated problem... Presumably, 'Mate' has some
sort of post-condition that must be fulfilled by the function. The
Mate(Mammal&) function above doesn't fulfill that post-condition.
 
S

Siemel Naran

JKop said:
I know the following would work, but it seems a bit inefficent to me:

#include <typeinfo>

virtual void Mate(Mammal &mammal)
{
//Wait a minute, I'm not mating unless
//it's with another dog!

try
{
Dog& doggy = dynamic_cast<Dog&>(mammal);

//Now perform mating
}
catch( std::bad_cast )
{

}
}


Any thoughts on this?

Why do you think the above is inefficient? You can implement double
dispatch, but it would probably be slower than the use of dynamic_cast.

Dog& doggy = dynamic_cast<Dog&>(mammal);

The above is not symmetric. The line does not throw an exception if mammal
is a class Dog or any derived from it. This allows a dog to mate with
another class that is derived from dog. Thus

dog.mate(deriveddog);

would work without throwing a bad_cast exception. Yet deriveddog.mate(dog)
would throw a bad_cast exception.

Maybe the correct thing is to use typeid.


As a matter of style, I'd make Mate a non-member function that checks if the
types are compatible, then calls the virtual function of the class, which
should be private or protected. These virtual functions may assume that the
types are compatible, and can use static_cast to convert a Mammal& to a
Dog&.

void mate(const Mammal& lhs, const Mammal& rhs) {
assert(typeid(lhs) != typeid(rhs));
return lhs.mate(rhs);
}

void Dog::mate(const Mammal& rhs) {
const Dog& that = static_cast<const Dog&>(rhs);
}
 
A

Alexey Rusakov

How about?

#include <iostream>
#include <typeinfo>

class Mammal
{
public:
virtual void Mate(Mammal &m) = 0;
};

class Dog : public Mammal
{
public:
virtual void Mate(Mammal &m)
{
if(typeid(*this)==typeid(m))
{
std::cout << "ok" << std::endl;
}
else
{
std::cout << "no chance your not my type" << std::endl;
}
};
};

Looks awful. This is unscalable, inefficient and dangerous since few look
at stdout when mating :)

What I'd suggest is declare a non-member template function with some
overloads if needed:

class Mammal
{
//...
};
class Dog : public Mammal
{
//...
}

template <typename _Animal>
void Mate(_Animal & he, _Animal & she)
{
//... some default code
}

void Mate(Dog & he, Dog & she) // May be a friend of class Dog if needed
{
//...
}

Obviously, Mate(Dog &, Dog &) will be called with (Bulldog &, Collie &)
but this is not a crime, is it? :) In some cases this is not desirable
however, so you can use inheritance checking described in the book of Herb
Sutter and used in Boost library (hint: this library is documented well).
 

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

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top