2014-03-07 75 views
1

我有以下两类:覆盖指针到成员函数

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; 
    } 
}; 

而且我想使用它们像这样:

B b; 
b.a(); 

,并得到以下被称为B::b()

当然这是不是编译为B::b不是void(*)()类型。

我该如何让它工作?

UPDATE。谁问谁“为什么?”和“为了什么?”。
A类是一个非常基础的类,在生产代码中有许多后继者。 B类是第6个继任者,我想扩展A(最方便的地方),以便有更多的方法(来自B)可以存在,并且可能不在A和B的另一个继任者中。
虚拟方法与空身体可以采用,但它是丑陋的,我想避免它。抽象方法更是如此(因为现有的派生后继代码)。
我不想使用void(*)()类型的外部函数来不释放对所有层次结构的内部数据的访问。

+0

你可能想看看['标准:: function'(http://en.cppreference.com/w/cpp/utility/functional/function)和['std :: bind'](http://en.cppreference.com/w/cpp/utility/functional/bind)。 –

+1

你的问题是什么?到目前为止,你有一个声明 – harmic

+0

我的问题是“如何?” – OlegG

回答

1

好吧,可能不是这次练习的目的,但是如果你想让它工作,你可以简单地声明static void b()

另一种选择是声明friend void b(),但打印输出将显示错误的事实。

+1

后者将无法正常工作,因为OP似乎有意解决这个问题。这不是交友'b()'的问题。它是* type *的问题。一个'void(A :: * ptr)()'与'void(B :: * ptr)()'不是类型相等的,并且没有多少朋友会改变它。 – WhozCraig

+0

@WhozCraig,正如答案的开头所述:“可能不是这次练习的目的”...... –

2

现在您的课程已定义,您无法使其工作。

调用另一个类的非静态成员函数需要该类的一个实例。您在存储函数指针时需要存储对拥有成员函数的对象的引用,或者在拨打A::a时向对象传递引用。

您还需要声明m_ptr,其类型为void (B::*)(),它指向B的成员,该函数是不带参数并返回void的函数。

请看下面的例子:

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; 
    } 
}; 

现在,我们可以称之为B::b这样的:

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

你使用这种方式的传承的是混乱的,因为这意味着,真正的问题你试图解决的是通过基类invoka派生类的成员。这通常是用一个简单的虚函数这样完成的:

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; 
    } 
}; 

而且像这样使用:

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

我知道上层课程中有一个空功能体的解决方案。但我想避免它。 我想可以有解决方案使用一些技巧与类范围或类似的东西。 – OlegG

0

我建议,因为要避免虚拟机制使用CRTP。但是,请注意,您的代码可能需要进行一些设计更改以适应此模式。但它确实提供了类型安全性,并且没有运行时间开销。希望能帮助到你。

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); 
} 

程序输出:

foo_A : foo_B 
foo_B 
foo_C 
+0

基类A在生产代码中有许多衍生物。在这样的条件下,我没有看到用班级模板安全替换班级的可能性。 – OlegG

+0

我不明白你为什么会用“安全”这个词。这是安全的。它需要一些工作,具体取决于实际的代码......如果你不想运行时多态性,但想要编译时多态性,那么它就是你想要的CRTP。如果你真的想要动态分派,你需要虚拟功能。否则,我会说,你正在发明一个轮子。而且发明和维护新的车轮版本会花费更多。 – lapk