|
-
February 9th, 2011, 03:01 PM
#1
Multiple virtuality
Is it possible to make a virtual function behave differently based on the data it is given even if the latter is an abstract pointer and how?
I tried making something like that, the function calls look good enough, but the implementation is simplistic. I'd rather not have a switch statement in every concrete class derived form BaseB.
Code:
#include <iostream>
using namespace std;
class BaseA
{
public:
virtual char getType()=0;
};
class A1:public BaseA
{
public:
char getType()
{
return 'a';
}
};
class A2:public BaseA
{
public:
char getType()
{
return 'b';
}
};
class BaseB
{
public:
virtual void use(BaseA*)=0;
};
class B1:public BaseB
{
public:
void use(BaseA* basea)
{
switch(basea->getType())
{
case 'a':
handle((A1*)basea);
break;
case 'b':
handle((A2*)basea);
break;
default:
break;
}
}
void handle(A1* a1)
{
cout<<"B1 uses A1"<<endl;
}
void handle(A2* a2)
{
cout<<"B1 uses A2"<<endl;
}
};
class B2:public BaseB
{
public:
void use(BaseA* basea)
{
switch(basea->getType())
{
case 'a':
handle((A1*)basea);
break;
default:
break;
}
}
void handle(A1* a1)
{
cout<<"B2 uses A1"<<endl;
}
void handle(A2* a2)
{
cout<<"B2 uses A2"<<endl;
}
};
int main()
{
BaseA* baseA[2]={new A1, new A2};
BaseB* baseB[2]={new B1, new B2};
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
baseB[i]->use(baseA[j]);
return 0;
}
Last edited by wasd; February 9th, 2011 at 03:03 PM.
-
February 9th, 2011, 04:00 PM
#2
Re: Multiple virtuality
Why would you want to do that? Polymorphism will handle that for you!
Well I have seen it done with MFC and CObject, apparently when you derive from CObject you can get type info. I have seen case statements like that, but I personally believe C++ is designed to avoid just this sort of thing.
ahoodin
To keep the plot moving, that's why.

-
February 9th, 2011, 05:16 PM
#3
Re: Multiple virtuality
I don't know how to make the use() of BaseB act different depending on the data it is given. I want to make use() react differently if I pass it either A1* or A2* via BaseA*. Switch is the best I could come up with.
-
February 9th, 2011, 05:38 PM
#4
Re: Multiple virtuality
They already act differently, otherwise your call to getType wouldn't have worked.
I.e. all you have to do is to declare the common interface for A1 and A2 in BaseA. Whenever a user (with knowledge of only BaseA) gets an object (of either A1 or A2) and calls a method the correct object will be called (just as getType is).
-
February 9th, 2011, 06:36 PM
#5
Re: Multiple virtuality
Well yeah, but I ended up using switch(basea->getType()) to call the functions handle(A1*) or handle (A2*). I would like that use(BaseA*) would call the specific overloaded handle() without switch.
-
February 10th, 2011, 09:39 AM
#6
Re: Multiple virtuality
You can achieve this by using a double dispatch, which is often achieved using the Visitor pattern.
One way of implementing this is as follows:
Code:
#include <iostream>
using namespace std;
class A1;
class A2;
struct AVisitor
{
virtual void Visit(A1*) = 0;
virtual void Visit(A2*) = 0;
};
class BaseA
{
public:
virtual void Accept(AVisitor& visitor) = 0;
};
class A1:public BaseA
{
public:
virtual void Accept(AVisitor& visitor)
{
visitor.Visit(this);
}
};
class A2:public BaseA
{
public:
virtual void Accept(AVisitor& visitor)
{
visitor.Visit(this);
}
};
class BaseB : public AVisitor
{
public:
virtual void use(BaseA*)=0;
};
class B1:public BaseB
{
private:
void Visit(A1* a1)
{
cout<<"B1 uses A1"<<endl;
}
void Visit(A2* a2)
{
cout<<"B1 uses A2"<<endl;
}
public:
void use(BaseA* basea)
{
basea->Accept(*this);
}
};
class B2:public BaseB
{
private:
void Visit(A1* a1)
{
cout<<"B2 uses A1"<<endl;
}
void Visit(A2* a2)
{
cout<<"B2 uses A2"<<endl;
}
public:
void use(BaseA* basea)
{
basea->Accept(*this);
}
};
int main()
{
BaseA* baseA[2]={new A1, new A2};
BaseB* baseB[2]={new B1, new B2};
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
baseB[i]->use(baseA[j]);
return 0;
}
This works by calling the virtual Accept function of the BaseA class (dispatching to the appropriate class derived from BaseA). The Accept function calls the Visit member of the visitor that has the matching BaseA derived parameter.
I made the BaseB class derive from the AVisitor class since this is just a simple case. This shows how you can implement a double-dispatch. If you want to have multiple virtual routines in the BaseA classes which can be called from BaseB, you can separate the visitor implementation into its own class.
Note that I removed the getType() member since it isn't needed anymore.
-
February 10th, 2011, 04:37 PM
#7
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|