简体   繁体   中英

Override pointer-to-member-function

I have these two classes:

class A {
public:
    A() { m_ptr = NULL; }
    void (*m_ptr)();
    void a() { if (m_ptr) m_ptr(); }
};

class B : public A {
public:
    B() { m_ptr = b; }
    void b() {
        std::cout << "B::b() is called" << std::endl;
    }
};

And I want to use them like this:

B b;
b.a();

and get the following to be called B::b() .

Of course this is not being compiled as B::b is not of type void(*)() .

How can I make it work?

UPDATE. To whom who asks "why?" and "what for?".
The class A is a very basic class which has many successors in production code. The class B is 6-th successor and I want to extend A (the most convinient place) to call there one more method (from B) which can be present and may be not in another successors af A and B.
A virtual method with empty body can be employed for that but it is ugly and I want to avoid it. Abstract method even more so (because of existing derived successors code).
I don't want to use external function of type void (*)() to not loose access to internal data of all hierarchy.

You can't make it work as your classes are defined now.

Calling a non-static member function of another class requires an instance of that class. You either need to store a reference to the object that owns the member function when storing the function pointer, or pass a reference to the object when you make the call to A::a .

You also need to declare m_ptr with the type void (B::*)() , which is pointer to member of B that is a function taking no parameters and returning void .

Look at this example:

class A {
public:
    A() { m_ptr = nullptr; }
    void a(B& b) { if (m_ptr) (b.*m_ptr)(); } // Now takes reference to B object.
    void (B::*m_ptr)();                       // Pointer to member function of B.
};

class B : public A {
public:
    B() { m_ptr = &B::b; } // Adress of qualified function.
    void b() {
        std::cout << "B::b() is called" << std::endl;
    }
};

Now we can call B::b like this:

B b;
b.a(b); // Pass reference to b when calling.

Your use of inheritence in this way is confusing as it implies that the real problem you are trying to solve is to invoka a member of a derived class through the base class. This is usually accomplished using a simple virtual function like this:

class A {
public:
    virtual ~A() {}
    void a() const { b(); } // Call b.
private:
    virtual void b() const {}
};

class B : public A {
public:
    virtual void b() const override { // C++11 override specifier (optional).
        std::cout << "B::b() is called" << std::endl;
    }
};

And used like this:

B b;
b.a(); // B::b is called.

Well, probably not the purpose of this exercise, but you can simply declare static void b() if you want to make it work.

Another option is to declare friend void b() , but then the "B::b() is called" printout would be stating a wrong fact.

I would suggest using CRTP since you want to avoid virtual mechanism. Note, however, your code might require some design changes to accommodate this pattern. But it does provide type safety and has no run-time overhead. Hope it helps.

Code on ideone.com :

#include <iostream>
#include <type_traits>

namespace so {

class B;

template<typename T>
class A {
 public:
  template<typename U = T, typename = typename std::enable_if<std::is_same<U, B>::value>::type>
  void foo_A() {
   std::cout << "foo_A : ";
   static_cast<U *>(this)->foo_B();
  }
};

class B: public A<B> {
 public:
  void foo_B() {
   std::cout << "foo_B" << std::endl;
  }
};

class C: public A<C> {
 public:
  void foo_C() {
   std::cout << "foo_C" << std::endl;
  }
};
} // namespace so

int main() {
 so::B b_;
 so::C c_;

 b_.foo_A();
 b_.foo_B();

 //c_.foo_A(); Compile error: A<C>::foo_A() does not exist!
 c_.foo_C();

 return (0);
}

Program output:

foo_A : foo_B
foo_B
foo_C

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM