指针成员和成员函数的最佳用途是什么?

时间:2009-04-08 18:21:05

标签: c++

成员指针不是很常用,但它们非常强大,你如何使用它们以及你做过的最酷的事情是什么?

修改 这并不是列出可能的事情,例如列出 boost :: bind boost :: function 并不好。相反,也许他们很酷的用法?我知道他们自己很酷,但这不是这个。

8 个答案:

答案 0 :(得分:5)

我曾经需要使用标准数据作为纯结构进行操作,以便能够存储队列中所有条件的列表。我不得不将结构与GUI和其他过滤器元素等绑定在一起。所以我想出了一个解决方案,其中使用指向成员的指针以及指向成员函数的指针。

假设你有一个

struct Criteria
{
    typedef std::string Criteria::* DataRefType;
    std::string firstname;
    std::string lastname;
    std::string website;
};

您可以使用

包装条件字段并使用字段的字符串表示形式进行绑定
class Field
{
public:
    Field( const std::string& name,
           Criteria::DataRefType ref ):
        name_( name ),
        ref_( ref )
    {}
    std::string getData( const Criteria& criteria )
    {
        return criteria.*ref_;
    }
    std::string name_;
private:
    Criteria::DataRefType ref_;
};

然后您可以随时注册所有要使用的字段:GUI,序列化,按字段名称比较等。

class Fields
{
public:
    Fields()
    {
        fields_.push_back( Field( "First Name", &Criteria::firstname ) );
        fields_.push_back( Field( "Last Name", &Criteria::lastname ) );
        fields_.push_back( Field( "Website", &Criteria::website ) );
    }
    template < typename TFunction >
    void forEach( TFunction function )
    {
        std::for_each( fields_.begin(), fields_.end(),
                       function );
    }
private:
    std::vector< Field > fields_;
};

通过调用实例fields.forEach( serialization );

GuiWindow( Criteria& criteria ):
    criteria_( criteria )
{
    fields_.forEach( std::bind1st( 
                         std::mem_fun( &GuiWindow::bindWithGui ),
                         this ) );
}
void bindWithGui( Field field )
{
    std::cout << "name " << field.name_
              << " value " << field.getData( criteria_ ) << std::endl;
};

答案 1 :(得分:3)

指向成员函数的指针非常适合使用for_each

创建伪lambda表达式
vector<SomeClass*> v = getAVector();
for_each(v.begin(), v.end(), mem_fun(&SomeClass::print));

答案 2 :(得分:3)

我做过的最酷的事情,我很久以前做过的。今天可能有更好的方法。

我为网络管理工具创建了一个自生成的命令行解析器。表示要管理的对象的类都有自己的子类表(名称,指向工厂成员的指针),实例(id,指向列表中实例的指针)和命令(名称,指向成员函数的指针) )。这使得解析器可以处理以下内容:

SET NETWORK ROUTE 192.168.0.0 HOPS 1

QUERY NETWORK NAMESERVER servername

不了解路线或名称服务器。

答案 3 :(得分:1)

我使用标准算法定期使用指向成员函数的指针。就我而言,他们没什么特别的。

答案 4 :(得分:1)

您可以使用boost :: bind绑定成员变量和函数,并获得通常的仿函数 下一步与他们合作将喜欢通常的功能用法:

  • 作为回调或信号功能传递;
  • 在标准算法中使用;
  • 在std :: map / set中使用比较器;
  • 用作数据访问者;

答案 5 :(得分:0)

除了上一个之外,您还可以将它们用作回调函数。

答案 6 :(得分:0)

我在“DomainEditor”类中为我编写的这个庞大的应用程序做过。数据库中的所有类型(域)表都可以由程序的管理员编辑,并且由于客户端使用不同于其他类型的名称调用某些类型,我创建了一个允许您编辑它们的对话框。好吧,我不想为15+域类型编写一个编辑器,所以我编写了一个可以将每个类投射到的超类,并且使用指针我可以对每个域表进行简单的调用。每个都支持所有相同的属性,描述(名称),ID,非活动标志和必需标志。因此,代码以宏开始设置我的调用:

#define DomainList(Class, Description, First, Next, Item, UpdateItem, DeleteItem, IsItemRequired, MaxLength) { \
   CWFLHandler *handler = new CWFLHandler; \
   handler->pWFL = new Class;\
   handler->LoadFirstType = (LoadFirst)&Class::First;\
   handler->LoadNextType  = (LoadNext)&Class::Next;\
   handler->LoadType      = (Load)&Class::Item;\
   handler->UpdateType    = (Update)&Class::UpdateItem;\
   handler->DeleteType    = (Delete)&Class::DeleteItem;\
   handler->IsRequiredType= (IsRequired)&Class::IsItemRequired; \
   handler->MAX_LENGTH    = MaxLength;\
   PopulateListBox(m_Domain, Description, (long)handler); }\

然后,大量调用宏:(这里只是一个)

   DomainList(CConfigWFL,   "Application Parameter Types",        LoadFirstParameterType,                   LoadNextParameterType,                 LoadParameterTypeByTypeId,                                        UpdateParameterType,                DeleteParameterType,                IsParameterTypeRequired,            LEN_APPL_PARAMETER_DESC);

然后,编辑数据的调用都很常见,我根本不需要复制任何代码......

例如,要使用DropDownList中的所选项填充列表(由宏填充),代码将如下所示:

 if((pWFLPtr->pWFL->*pWFLPtr->LoadFirstType)(true))
  {
     do
     {
        m_Grid.AddGridRow();
        m_Grid.SetCheck(COLUMN_SYSTEM,         (pWFLPtr->pWFL->*pWFLPtr->IsRequiredType)(pWFLPtr->pWFL->TypeId));
        m_Grid.SetCheck(COLUMN_STATUS,      pWFLPtr->pWFL->InactiveIndc == false);
        m_Grid.AddTextToGrid(COLUMN_NAME,   pWFLPtr->pWFL->TypeDesc);
        m_Grid.AddTextToGrid(COLUMN_DEBUG,  pWFLPtr->pWFL->TypeId);
        m_Grid.AddTextToGrid(COLUMN_ID,     pWFLPtr->pWFL->TypeId);
     }
     while((pWFLPtr->pWFL->*pWFLPtr->LoadNextType)());

当然,这些都存储在一个属于对话框的类中。我只是创建了类的新实例,将它们存储在ListBox的ItemData成员中。因此,当对话框关闭时,我确实必须清理所有这些。但是我将该代码留在了此消息之外。

存储所有这些内容的类定义为:

   typedef bool (CMyWFL::*LoadFirst)(bool);
   typedef bool (CMyWFL::*LoadNext)();
   typedef bool (CMyWFL::*Load)(long);
   typedef bool (CMyWFL::*Update)(long, const char*, bool);
   typedef bool (CMyWFL::*Delete)(long);
   typedef bool (CMyWFL::*IsRequired)(long);

   class CWFLHandler {
   public:
      CWFLHandler()  {};
      ~CWFLHandler() { if(pWFL) delete pWFL; }

      CMyWFL    *pWFL;
      LoadFirst   LoadFirstType;
      LoadNext    LoadNextType;
      Load        LoadType;
      Update      UpdateType;
      Delete      DeleteType;
      IsRequired  IsRequiredType;
      int         MAX_LENGTH;
   };
   CWFLHandler *pWFLPtr;

所有这些工作使得能够将新域添加到应用程序非常好,只需要很少的工作就可以将它们添加到域编辑器中......可能有更好的方法,我不知道。但这就是我去的方式,它对我来说非常好,恕我直言,它非常有创意...... :)

答案 7 :(得分:0)

我使用它们作为StructSerlialiser的一部分,从SAX Parser事件中填充C ++ POD结构,即将XML映射到C ++数据模型。

template<class STRUCT, typename FIELDTYPE>
struct FieldBinderImpl : public FieldBinder<STRUCT>
{
    typedef FIELDTYPE (STRUCT::*MemberPtr);

    FieldBinderImpl (const std::string& tag, MemberPtr memberPtr)
        : FieldBinder (tag)
        , memberPtr_ (memberPtr)
    {
    }

    virtual SerialiserBase* createSerialiser (STRUCT& data) const
    {
        return new Serialiser<FIELDTYPE> (&(data.*memberPtr_));
    }

private:
    MemberPtr memberPtr_;
};

template<class T>
class StructSerialiser : public SerialiserData<T>
{
public:
    typedef std::vector<FieldBinder<T>*> FieldBinderList;

private:
    static FieldBinderList fieldBinderList_;

protected:
    template<typename FIELDTYPE>
    static void bind (const std::string& tag, FIELDTYPE (T::* member))
    {
        fieldBinderList_.push_back (new FieldBinderImpl<T, FIELDTYPE> (tag, member));
        if (tag.empty ())
            fieldBinderList_.back ()->tags_ = Serialiser<FIELDTYPE>::getTags ();
    }

    // ...
}

// ...

还有用于双重,字符串,向量等的Serialiser。要使用它,您只需将struct成员绑定到名称,例如:

class Index
{
public:
    std::string         currency;
    std::string         name;
};

template<>
class Serialiser<Index> : public StructSerialiser<Index>
{
public:
    Serialiser (Index* data) : StructSerialiser<Index> (data) {}

    static void initialise ()
    {
        bind ("currency", &Index::currency);
        bind ("name", &Index::name);
    }
};