使用指向类私有方法的指针的命名参数惯用语

Posted

技术标签:

【中文标题】使用指向类私有方法的指针的命名参数惯用语【英文标题】:Named Parameter Idiom using a pointer to a class private method 【发布时间】:2015-10-20 12:38:03 【问题描述】:

我在做一些可能不是真正“传统”的事情时遇到了 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”可以使用命名参数惯用语来初始化,使用类“init”的方法“has_funcPtr”连接到其构造函数,例如:“init().has_funcPtr(&function_name)”。 “user”类可以将指向其私有方法“func1”的指针存储为“item”类型的私有成员的私有成员。

这样,当调用对象“item”的特定方法时(为简单起见,这里不包含这么长的部分,因为它与错误无关,而只是描述这个sn的目标-p of code)该方法可以做一些事情并通过指向函数的指针调用其父对象“用户”的私有方法(我希望这足够清楚......)。

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

提前谢谢大家

【问题讨论】:

如果将来有人想重用此代码,请发表评论。在“init”类的方法“has_funcPtr”中,我忘记返回对象本身。它应该是: init& has_funcPtr(funcPtr fp) m_fp = fp;返回 *this; 【参考方案1】:

this-&gt;func1 不构成成员函数指针。如果您在 user 类中,它应该看起来像 &amp;user::func1

【讨论】:

谢谢你,令人难以置信的是,我能在如此基本的事情上浪费这么多时间......不过还有一件事。如果我使用 &user::func1 我是否使用指向该实例方法的指针?因为一开始我认为“has_funcPtr(func1) 应该可以工作,因为我已经在“用户”范围内,如果我想访问实例的方法,我应该以某种方式使用 this 指针。 成员函数指针不绑定到特定的实例,因此在使用指针时还需要将其与实例结合。见How do I call a pointer-to-member function?【参考方案2】:

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

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

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

    (object_ptr-&gt;*method_ptr)(parameters_here)

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

    #define CALL_MEMBER_FN(ptrToObject,ptrToMember) ((ptrToObject)-&gt;*(ptrToMember))

    然后将其用作:

    CALL_MEMBER_FN(object_ptr, method_ptr)(parameters_here)

    在第一点之后,如果您希望嵌套类能够通过指向它的指针调用上层类方法,您还需要传递上层类实例指针来访问该函数。就我而言,由于我希望能够逐个决定是否应该调用该方法,因此我使用了命名参数惯用语(例如,请注意以下 func2 未注册)。

最后是修改后的代码,它可以工作(测试):

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

【讨论】:

以上是关于使用指向类私有方法的指针的命名参数惯用语的主要内容,如果未能解决你的问题,请参考以下文章

变量,方法,类命名规则

c++重用和模板

错误:未命名类型,将指向类的指针设置为 0

指向类方法的函数指针作为参数

20180724 (面向对象:类的命名空间和查询顺序丶组合)

模板函数将参数函数指针指向类方法