Calling derived class through base class function pointer

狂风中的少年 提交于 2020-01-22 18:59:25

问题


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 cv1 T” can be converted to a prvalue of type “pointer to member of B of type cv2 T”, where B is a base class (Clause 10) of D, 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 class B 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 of E1 does not contain the member to which E2 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!