问题
Can I call a derived class through a base class function pointer, as shown in the example below?
I understand that my example works, but is it guaranteed to always do so (Assuming the object actually implements the function!), or is this just an idiosyncrasy of the compiler I'm using?
By this logic can't one simply derive all their classes from "CBase" (which in this case is empty so I guess no overhead) and ignore the type in the function pointer?
#include <iostream>
struct CBase
{
};
struct CDerived : CBase
{
void MyFunction()
{
std::cout << "Called OK" << std::endl;
}
};
typedef void (CBase::*FunctionPointer)();
int main()
{
CDerived* base = new CDerived();
FunctionPointer pointer = static_cast<FunctionPointer>(&CDerived::MyFunction);
(base->*pointer)();
delete base;
}
Example usage scenario: A derived class that takes one or more pointers to "callbacks" in the base class. Can the callback type just be defined using the derived class and thus forgo the need for a template?
回答1:
Yes, it's guaranteed to work. From [expr.static.cast]:
A prvalue of type “pointer to member of
D
of type cv1T
” can be converted to a prvalue of type “pointer to member ofB
of type cv2T
”, whereB
is a base class (Clause 10) ofD
, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.70 If no valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), the program is ill-formed. The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If classB
contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member.
In this case, we're converting a pointer to member of CDerived
of type void()
to a poitner to member of CBase
ov type void()
. CBase
is a base of the class containing the original member, so the resulting pointer points to the original member.
From [expr.mptr.oper]:
Abbreviating pm-expression.*cast-expression as
E1.*E2
,E1
is called the object expression. If the dynamic type ofE1
does not contain the member to whichE2
refers, the behavior is undefined.
In this case, pointer
pointers to the original member. base
has that member. So this is fine.
Note that in your example, base
is actually a CDerived*
. It would be just as valid to write:
CDerived d;
CBase* b = &d;
(b->*pointer)(); // ok - the dynamic type of b contains the member to which pointer refers
回答2:
You can use this method but casting pointers is not a good practice at all. Method you are using has nothing common with polymorphism. A better way to implement this is using a virtual
function and a pure
function.
Your base class must have a pure function (pure functions also virtual) and your derived class must implement this function. So you will be able to call this function with base pointer and implementation of your derived class will be called.
#include "stdafx.h"
#include <iostream>
struct CBase
{
virtual void MyFunction() =0; // this is a `pure` function. Pure means it's a virtual and may not have implementation. If a class has at least one pure function it means this class is an abstract class
virtual ~CBase()=default; // gotta be or we will have a memory leak during `delete`..
};
struct CDerived : CBase
{
virtual void MyFunction() override
{
std::cout << "Called OK" << std::endl;
}
};
//typedef void (CBase::*FunctionPointer)();
int main()
{
CBase* base = new CDerived();
base->MyFunction();
delete base;
system("pause");
return 0;
}
来源:https://stackoverflow.com/questions/37662100/calling-derived-class-through-base-class-function-pointer