命名参数Idiom使用指向类私有方法

时间:2015-10-20 12:38:03

标签: c++ function-pointers initialization-list in-class-initialization initialization-order

在做一些可能不是“常规”的事情时,我遇到了C ++编译错误。 为了使事情变得更容易,我只是重新编写了我试图以更易于阅读的方式使用的机制,并且检查了我遇到了同样的问题。

首先是代码:

test.h // - - C ++ - -

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp;}
  init() {}
  virtual ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  item(init<MODULE> params) : m_fp(params.m_fp) {}
  virtual ~item() {}
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Method CB
  int func1(int i);
  // Item member
  item<user> m_item;
public:
  user();
  virtual ~user();
};

test.cpp // - - C ++ - -

#include "test.h"

user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {}

int user::func1(int i) {return 1;}

这是错误:

/test.cpp:5:59: error: invalid use of non-static member function
 user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {
                                                       ^

所以,我不确定这是实现我想要的最佳方式(可能不是,无论如何,如果你有其他建议,我们非常欢迎),但我现在的目标是让它工作或理解为什么它可以工作,以便我从中学到一些东西!

基本理念是:

  • 类“item”可以使用命名参数idiom初始化,使用类“init”的方法“has_funcPtr”连接到其构造函数,如:“init()。has_funcPtr(&amp; function_name)”。
  • 类“user”可以将指向其私有方法“func1”的指针存储为其“item”类型的私有成员的私有成员。

这样,当调用对象“item”的特定方法时(为简单起见,我不在此处包含这个长篇部分,因为它与错误无关,但它只是描述了这个片段的目标)代码)该方法可以执行操作并通过该指针函数调用其父对象“user”的私有方法(我希望这很清楚......)。

现在,我认为对象的初始化顺序存在问题,但我不确定在何处以及如何解决它。 特别是我认为,由于“func1”方法不能对“user”类的任何成员进行操作,因此可以直接在初始化列表中使用它的引用来初始化“init”对象并将其提供给“item” “对象。

提前谢谢大家

2 个答案:

答案 0 :(得分:4)

this->func1不构成成员函数指针。如果您在&user::func1班级,它应该看起来像user

答案 1 :(得分:0)

我在这里发布了我的问题的完整答案。我是在Bo的建议之后开发的,并在了解了如何通过指向它的指针指向实例特定方法之后。

简而言之,有两点非常重要:

  1. 指向非静态类成员函数的指针可以被认为只是一个偏移而不是“绝对地址”(http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm)。这意味着如果没有实例指针,则无法访问该函数(它只是一个偏移量)。一旦有了实例指针,使用这个“偏移指针”就可以使用:

    调用该方法

    (object_ptr->*method_ptr)(parameters_here)

    更好的方法是使用#define宏,因为这种语法非常容易出错并且阅读起来很复杂(https://isocpp.org/wiki/faq/pointers-to-members):

    #define CALL_MEMBER_FN(ptrToObject,ptrToMember) ((ptrToObject)->*(ptrToMember))

    然后将其用作:

    CALL_MEMBER_FN(object_ptr, method_ptr)(parameters_here)

  2. 在第一点之后,如果希望嵌套类能够通过指向它的指针调用上层类方法,则还需要传递上层类实例指针来访问该函数。在我的情况下,因为我希望能够逐个判断是否应该调用该方法,所以我使用了命名参数成语(下面注意func2未注册)。

  3. 最后,这是修改后的代码(测试):

    - - C ++ - - test.h

    #include <iostream>
    
    template <typename MODULE> class item;
    
    template <typename MODULE>
    class init {
    public:
      typedef int  (MODULE::*funcPtr)(int);
      typedef bool (MODULE::*func2Ptr)(bool);
    private:
      funcPtr  m_fp;
      func2Ptr m_fp2;
      MODULE* m_dad;
    public:
      init& has_funcPtr(funcPtr fp) { m_fp = fp; return *this;}
      init& has_func2Ptr(func2Ptr fp2) { m_fp2 = fp2; return *this;}
      init(MODULE* dad) : m_dad(dad) { std::cout << "init constructor called\n"; }
      ~init() {}
    private:
      friend class item<MODULE>;
    };
    
    template <typename MODULE>
    class item {
    public:
      typedef int  (MODULE::*funcPtr)(int);
      typedef bool (MODULE::*func2Ptr)(bool);
    private:
      funcPtr  m_fp;
      func2Ptr m_fp2;
      MODULE*  m_dad;
    public:
      item(init<MODULE> params) :
        m_fp(params.m_fp),
        m_fp2(params.m_fp2),
        m_dad(params.m_dad)
      {
        std::cout << "item constructor called\n";
      }
      ~item() {}
      // Method invoked externally
      int callback() {
        std::cout << "item class method callback invoked\n";
        // In the real case here do general stuff
        if(m_fp) {
          int i = (m_dad->*m_fp)(1); // call member function through its pointer
          return i;
        } else {
          std::cout << "callback not registered\n";
          return 0;
        }
      }
      // Method invoked externally
      bool callback2() {
        std::cout << "items class method callback2 invoked\n";
        // In the real case here do general stuff
        if(m_fp2) {
          bool b = (m_dad->*m_fp2)(true); // call member function through its pointer
          return b;
        } else {
          std::cout << "callback2 not registered\n";
          return false;
        }
      }
    };
    
    class user {
    public:
      typedef init<user>::funcPtr funcPtr;
    private:
      // Methods that optionally add more functionalities to the 2 callbacks
      int  func1(int i);
      bool func2(bool b);
    public:
      // Item member
      item<user> m_item;
    public:
      user();
      ~user();
    };
    

    - - C ++ - - test.cpp

    #include "test.h"
    
    user::user() : m_item(init<user>(this).has_funcPtr(&user::func1) ) {
      std::cout << "user constructor called\n";
    }
    
    int user::func1(int i) {return i;}
    bool user::func2(bool b) {return b;} // func2 won't be registered
    
    
    int main() {
      user* u = new user();
      // Test callbacks
      int i = u->m_item.callback();
      bool b = u->m_item.callback2();
      std::cout << "main is printing i=" << i << " and b=" << b << "\n";
      std::cout << "expected results are i=1 and b=0\n" << "END\n";
      return 0;
    }
    

    输出:

    init constructor called
    item constructor called
    user constructor called
    item class method callback invoked
    items class method callback2 invoked
    callback2 not registered
    main is printing i=1 and b=0
    expected results are i=1 and b=0
    END