Thank you very much for all of your help!
Regards, Freddie.
Thank you very much for all of your help!
Regards, Freddie.
I know C but am just teaching myself C++. The only way I got the code below
to work was by following the original example (and replies) and chasing
compiler error messages around as I permuted my brains out. It still seems
like a potentially type-unsafe situation, however.
In particular, this bit of the standard is a little worrying:
"although class B need not contain the original member, the dynamic type of
the object on which the pointer to member is dereferenced must contain the
original member"
I'm not sure what "dynamic type" means (I will look it up), but in the
code below does B's "dynamic type" contain the original member function?
If not, then since B seems to be "the object on which XXX is dereferenced",
it does not seem to conform.
At any rate, the code below works, but how would you connect the given template to it?
Is it correct that the template's purpose is to obviate the static casting in D:()?
(Hmm, I seem to have ended up with a big grin there. I was refering to D's constructor.)
Code:template <typename T> void callT( void (T::*mem_fun)() ) { call(static_cast<fPtr>(mem_fun)); } #include <iostream> using namespace std; class B { public: typedef bool (B::* fp_test_t) (); /* Test func type; returns bool */ typedef void (B::* fp_action_t) (); /* Action func type */ B() { nfp = 0; } /* Zero function pointer count */ void add (fp_test_t, fp_action_t); void match(); private: fp_test_t fp_test[10]; /* Arrays used for simplicity */ fp_action_t fp_action[10]; int nfp; /* Number of function pointers registered */ }; void B::add (fp_test_t test, fp_action_t act) { fp_test[ nfp ] = test; fp_action[ nfp ] = act; nfp++; } void B::match() { for (int i = 0; i < nfp; i++) if ((this->*fp_test[ i ])()) (this->*fp_action[ i ])(); } class D : public B { public: D(); bool test1(); void action1(); bool test2(); void action2(); }; D::D() { add( static_cast< B::fp_test_t >( &D::test1 ), static_cast< B::fp_action_t >( &D::action1 )); add( static_cast< B::fp_test_t >( &D::test2 ), static_cast< B::fp_action_t >( &D::action2 )); } bool D::test1() { return false; } void D::action1() { cout << "d1\n"; } bool D::test2() { return true; } void D::action2() { cout << "d2\n"; } int main() { D d; d.match(); }
Last edited by oogabooga; 01-11-2008 at 06:07 PM.
Well, as you know, not everything is always inherited from the base class. This is true if functions or member data is marked as private.
If data or functions are marked as public or protected, they're inherited, thus class B which inherits from A would have all of A's member functions. But if those functions are private, B cannot inherit them.
The problem stems that if you try to call a member function in A, the base class, and cast it to a pointer to call that function in the derived class, the function must exist! In other words, B must inherit the function from A, otherwise the result would be undefined.
It's perfectly fine for B not to inherit a function in A, but if you cast that pointer and use it, the object (of type B) must inherit that function from A, the original function member.
This, I believe, is what the standard says.
Correct me if I'm wrong, but are you saying (re-lettered to match the
standard where B is base and D is derived):
"If you call a member function in B through a pointer to D
then D must have inherited that function (lest it not exist)."
IF so, then you may be looking at it the wrong way round for this example.
Here we are calling a member function of D through a pointer to B,
not a member function of B through a pointer to D.
Everything is always inherited. Private members are merely invisible, but they're still there.
The dynamic type of an object is its actual type, whereas the static type is the type of the pointer/reference you're using to deal with it. E.g.:
The static type is B, because b is a pointer to B, but the dynamic type is D.Code:B *b = new D;
Given:
1) "The null member pointer value is converted to the null member pointer value of the destination type." (5.2.9/9)Code:struct Base { int i1; }; struct Derived : Base { int i2; }; int Base::*pbasei; int Derived::*pderivedi; Base b; Derived d; Base &rb = d;
2) "If class B contains the original member, [...] the resulting pointer to member points to the original member." (5.2.9/9)Code:pderivedi = 0; pbasei = static_cast<int Base::*>(pderivedi); assert(pbasei == 0);
3) "If class B [...] is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member." (5.2.9/9)Code:pderivedi = &Derived::i1; pbasei = static_cast<int Base::*>(pderivedi); assert(pbasei == &Base::i1); b.*pbasei = 10; assert(b.i1 == 10);
4) "If the dynamic type of the object does not contain the member to which the pointer refers, the behavior is undefined." (5.5/4)Code:pderivedi = &Derived::i2; pbasei = static_cast<int Base::*>(pderivedi); rb.*pbasei = 10; assert(d.i2 == 10);
Code:pderivedi = &Derived::i2; pbasei = static_cast<int Base::*>(pderivedi); b.*pbasei = 10; // UNDEFINED! b doesn't contain i2.
All the buzzt!
CornedBee
"There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
- Flon's Law
The original member function of the inherited class must exist in the derived class, but the point is kind of moot since, as CornedBee mentions, the functions are always inherited, even if private.
Therefore, it is always safe to do this, but might not be what you want.