question about method pointers and subclasses

Y

Yu Hu

Hello,

Is it legal (and moral) to assign a method pointer with the proper
prototype to a variable whose type involves the superclass? (Sorry if my
wording of the question is imprecise, I'm not sure what all the right
terminology is.) A code example should make it clear what I'm asking:

class A
{
public:
int Fn1( int x );
// ...
};

typedef int (A::*AMemFn)( int x );

class B : public A
{
public:
int Fn2( int x );
// ...
};

// Assume the methods are defined and the classes are completed elsewhere

AMemFn f = &B::Fn2; // Is this OK?

// Two examples of how I am using f:
B b_obj;
int r = (b_obj.*f)( 5 );

A *a_ptr = new B;
r = (a->(*f))( 5 );


In my code, I know that the type of the object and the class that the
method being stored in the variable agree. It still feels sketchy to me
because it seems like you could screw yourself by making an object of
type A and then calling a method of class B on it, which can cause bad
things to happen.

Visual C++ and g++ permit me to do this, but I wanted to know if this
generally should work.
 
G

Gianni Mariani

Yu said:
Visual C++ and g++ permit me to do this, but I wanted to know if this
generally should work.

Funny - after I remove some compiler errors, gcc says:

aaa.cpp:19: error: cannot convert `int (B::*)(int)' to `int (A::*)(int)' in

I suspect that you're out of luck. What you tried to do is illegal.

If you KNOW the object is a b then try this instead.

class A
{
public:
int Fn1( int x );
// ...
};


class B : public A
{
public:
int Fn2( int x ) { return 0; }
// ...
};

// Assume the methods are defined and the classes are completed elsewhere

typedef int (B::*BMemFn)( int x );
BMemFn f = &B::Fn2; // Is this OK?

// Two examples of how I am using f:
B b_obj;


int main()
{

int r = (b_obj.*f)( 5 );

A *a_ptr = new B;


r = ( ( static_cast<B*>( a_ptr ) )->*f)( 5 );
}

Having said that, I suspect you're doing somthing that you could
probably do with virtual methods. Why not use use vritual methods ?

G
 
A

Andrey Tarasevich

Yu said:
Is it legal (and moral) to assign a method pointer with the proper
prototype to a variable whose type involves the superclass? (Sorry if my
wording of the question is imprecise, I'm not sure what all the right
terminology is.) A code example should make it clear what I'm asking:

class A
{
public:
int Fn1( int x );
// ...
};

typedef int (A::*AMemFn)( int x );

class B : public A
{
public:
int Fn2( int x );
// ...
};

// Assume the methods are defined and the classes are completed elsewhere

AMemFn f = &B::Fn2; // Is this OK?

This will not compile. Member pointer types are contravariant, not
covariant. The following, however, is perfectly legal

AMemFn f = static_cast<AMemFn>(&B::Fn2);

There are obvious dangers in such conversions, so it is up to you to
decide whether it is OK or not OK to do it in your code.
// Two examples of how I am using f:
B b_obj;
int r = (b_obj.*f)( 5 );

A *a_ptr = new B;
r = (a->(*f))( 5 );

In the second case you probably meant

r = (a_ptr->*f)(5);

Both calls are fine, provided the 'f' pointer is initialized as shown above.
In my code, I know that the type of the object and the class that the
method being stored in the variable agree. It still feels sketchy to me
because it seems like you could screw yourself by making an object of
type A and then calling a method of class B on it, which can cause bad
things to happen.

Yes, that's the danger I was talking about.
Visual C++ and g++ permit me to do this, but I wanted to know if this
generally should work.

Yes, it is guaranteed to work by C++ standard.
 
Y

Yu Hu

Funny - after I remove some compiler errors, gcc says:

aaa.cpp:19: error: cannot convert `int (B::*)(int)' to `int (A::*)(int)' in

I suspect that you're out of luck. What you tried to do is illegal.

Oops. I typed the code from memory and forgot that it required an
explicit cast before it would compile, as the other response to my
original post pointed out. My mistake; in the future I'll run it through
a compiler before posting. :)
Having said that, I suspect you're doing somthing that you could
probably do with virtual methods. Why not use use vritual methods ?

This was the first C++ code added to the system, and the way we were
serializing our data would not have worked with objects with vtable
pointers.

I am implementing finite state machines. I wish to avoid using separate
classes for each state, although I've seen that pattern used in a lot of
places. There are many machines being processed simultaneously, with
many different types, so the system wants to call them only through the
common Machine superclass interface.

Machine::processInput needs to switch on the FSM's current state to call
the correct method to handle the input. I can only think of two ways
to do this. One is for ProcessInput to be virtual, and for each subclass
to override this method and use a switch statement there to call the
correct processing method for each state. The other is to store an array
of method pointers, and to index into it (assuming your state is an
offset into the array) to get the right method. Because I couldn't use
virtual methods (see above), I had to use the second option. Does anyone
know of a better way?

If I am allowed to use virtuals, I am definitely considering it. We're
moving our entire code base from C to C++ and other programmers want to
use virtual methods, so someone is going to have to solve the problems
with serialization and such. If so, the only remaining question is
whether writing a table of method pointers is less cumbersome than
writing a switch table to select the correct method.

Thanks for the response; thinking about using virtuals here has helped
shed some light on the problem.
 
Y

Yu Hu

This will not compile. Member pointer types are contravariant, not
covariant. The following, however, is perfectly legal

AMemFn f = static_cast<AMemFn>(&B::Fn2);

Oops. My mistake, I forgot the cast was necessary.
There are obvious dangers in such conversions, so it is up to you to
decide whether it is OK or not OK to do it in your code.

Yes, that's the main reason I am not entirely happy with it. So I guess
the question is whether I prefer to use the method pointers, or if I
prefer having virtual methods which switch on the state to select the
correct method to call (see my other post in this thread). The latter is
probably safer.
In the second case you probably meant

r = (a_ptr->*f)(5);

Oops again. Yes, I was just being sloppy.
Yes, it is guaranteed to work by C++ standard.

Thanks for the confirmation of that. I may decide not to use them, but I
still had an academic curiosity about whether or not it was ok.
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top