繁体   English   中英

将派生类中的成员函数的指针强制转换为抽象成员函数的指针

[英]cast a pointer to member function in derived class to a pointer to abstract member function

我正在尝试做一些看起来很普通的事情,但是我一直找不到有人在讨论它。 关于stackoverflow的这篇文章与我正在尝试的内容相似,但并不完全相同。

我有一个抽象基类:

#ifndef _ABASECLASS_H_
#define _ABASECLASS_H_

using namespace std;

#include <iostream>

#define CALL_MBR_FUNC(object, ptr_to_mem_func) ((object).*(ptr_to_mem_func))

class aBaseClass
{
public:

  typedef void (aBaseClass::*aBaseClass_mem_func)();

  int A;
  int B;

  aBaseClass();

  aBaseClass(int a, int b);

  virtual void function1(aBaseClass_mem_func infunc) = 0; 

  virtual void function2() = 0; 

};

#endif /* _ACLASS_H_ */

我有一个派生类:

#ifndef _ASUBCLASS_H_
#define _ASUBCLASS_H_

using namespace std;

#include <iostream>
#include "aBaseClass.h"


/* A simple class containing two ints and some functions to demonstrate passing via various methods.  It is a subclass of aClass*/
class aSubClass: public aBaseClass
{
public:

  aSubClass();

  aSubClass(int a, int b);

  void function1(aBaseClass_mem_func infunc); 

  void function2(void); 

};

#endif /* _ASUBCLASS_H_ */

其中function1和function2为:

void aSubClass::function1(aBaseClass_mem_func infunc) 
{
  CALL_MBR_FUNC(*this, infunc)();
}


void aSubClass::function2(void) 
{
  A = 42;
  B = 66;
}

最后,在main()我尝试调用function1的目标类型的对象上aSubClass ,一个指针传递给function2aSubClass

int main (int argc, const char * argv[])
{
  aSubClass eh(2,5);
// This doesn't work    
  aBaseClass_mem_func trythis = &aSubClass::function2;
// This also doesn't work
  eh.function1(&aSubClass::function2);

  return(0);
}

好的,我们可以自动将指针派生类型转换为指针到基类型。 我现在读到,我们不能将指向成员函数的指针传递给指向成员函数的指针。 我想我理解为什么(派生成员函数可能会利用派生类中存在的但基类中不存在的东西)。

但是我正在尝试构建一个包含两类类的库(从两个基类派生)。 将它们称为baseclass1和baseclass2。 必须能够将任何来自baseclass1的派生类中的成员函数之一交给来自baseclass2的任何派生类中的特定成员函数。 我可以使用一些技巧来进行必要的投射吗? 我必须使用explicit关键字并以某种方式定义演员表吗?

为什么不起作用:

您可以想到成员函数的一种方法是:

struct Foo {
    void go () { }
} ;

也可以表示为:

void go ( Foo* this ) { }

所以这:

typedef void(Foo::*MemberFunctionPtr)() ;

有点像这样:

typedef void(*MemberFunctionPtrForFoo)(Foo*) ;

但是,如果您有这样的子类:

struct Bar : public Foo {
    void go2 () { }
} ;

该函数也有点像这样:

void go2 ( Bar* this ) { }

因此,当您获取Bar::go2的地址时,基本上就是一个指向看起来像void go2 ( Bar* this )的函数的指针。 为什么这是个问题?

好吧,让我们看看这意味着什么...

如果您具有此功能:

void function ( Foo * this ) ;

而您要这样做:

Bar * bar = new Bar () ;
function ( bar ) ;

这将起作用(应如此)。 C ++可以帮助您实现以下目的:

void(*functionPtr)(Bar*) = &Foo::go ;

但是,可以说您拥有此功能:

void function ( Bar * this ) ;

而您这样做:

Foo * foo = new Foo() ;
function ( foo ) ;

这是行不通的,因为foo不一定是Bar 您可以static_cast ,这是告诉编译器“不,真的,我很确定我知道自己在做什么”的方式(相对于reinterpret_cast ,这是告诉编译器“您很愚蠢的方式”) ;我知道我在做什么。”)

因此,它也不会让您强制转换成员函数。

另一个答案说static_cast可以转换成员函数,但这仅是因为允许static_cast进行隐式static_cast转换的反向操作(cv限定除外)。 您可以执行此操作,但需要注意的地方相同。

免责声明:这是该规范的相当简化的版本,但它可以说明要点。

在大多数情况下,更好的解决方案是:

一个[潜在的]更好的解决方案[除非绝对性能是关键]: boost::function (或者从C ++ 11 std::function )。 这是一个“ functor”。

您的成员函数可以改为:

class Foo {
    void function ( boost::function<void()> function ) { }
} ;

functor对象可以使用可以用指定原型调用的任何东西(在这种情况下,不带任何参数并返回void )构造。 例如,您可以传递C函数的地址。

您可以做的另一件事是“绑定”函数(基本上是获取参数并创建一个函数)。 为此有boost::bind

例如,您可以这样做:

Foo foo ;
Bar bar ;
foo.function ( boost::bind(&Bar::go2,&bar) ) ;

增强绑定将某些功能作为第一个参数。 如果函数是成员函数,则下一个参数必须是可以在其上调用指定方法的类的实例(在这种情况下将被复制)或指向可以在其上调用指定方法的类的指针。引用)。 该示例实际上将导致foo实例调用bar实例(而不是本身),但是您可以改为传递&foo

您甚至可以更具创造力:

class Foo {
    void function ( boost::function<void(int)> function ) {
        function ( 1 ) ;
    }
    void go2 ( int a , int b ) {
        cout << a << " " << b << endl ;
    }
} ;
Foo foo ;
foo.function ( boost::bind(&Foo::go2,&foo,_1,2) ) ;

绑定抢:

  • Foo::go2的成员函数指针
  • foo实例的引用(或“指针”)
  • “结果函数的第一个参数”的占位符, go2成为go2调用中的第一个参数
  • 数字2,它将成为go2调用中的第二个参数

这是要打印到控制台的内容:

1 2

这是一个非常强大的工具,它将带您进入梦幻般的函数式编程世界,同时使您的生活更轻松。 (这也会使像@CortAmmon这样的人讨厌您。)

您可以大大简化此示例:

struct B {
    virtual void foo() = 0;
};

struct D : B {
    void foo() override { }
};

int main() {
    void (B::*ptr)() = &D::foo; // error: cannot initialize a variable of
                                // type 'void (B::*)()' with an rvalue of type 
                                // 'void (D::*)()': different classes ('B' vs 'D')
}

错误消息,至少在clang上,非常清楚。 gcc只是说无法初始化。 问题仅在于您不能将指针派生成员隐式转换为指针到基础成员。 但是您可以使用static_cast 明确地做到这一点

void (B::*ptr)() = 
    static_cast<void (B::*)()>(&D::foo); // ok!

旁注:请从您的代码中删除CALL_MBR_FUNC宏,再也不要写这样的东西。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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