【问题标题】:Reuse the same code to do the same logic on different data members of the objcets in a list重复使用相同的代码对列表中对象的不同数据成员执行相同的逻辑
【发布时间】:2013-01-12 22:51:38
【问题描述】:

具体来说,我有一个包含多个字符串对象数据成员(NID、customerNumber、studentNumber、fName、lName)的类的对象列表。

我想重用以下代码来搜索与搜索键匹配的节点,无论要查找的数据成员是 NID 还是任何其他类的字符串数据成员。

nodePtr = firstPtr;
for(; nodePtr != NULL && nodePtr->str != str; nodePtr = nodePtr->nextPtr);

if(nodePtr != NULL)
    //the nodePtr points to the node that matches the search key
else
    //no node matched the search key

如果是 PHP 代码,我可以使用一个变量的值作为另一个变量的名称:

$node->${$var}

但是在 C++ 中是否有重用代码?

【问题讨论】:

  • 预处理器宏?模板?
  • @JoachimPileborg 我如何使用模板?模板的参数是类型而不是变量名的占位符!
  • @csstd:模板参数可以是值而不是类型;特别是,它们可以是指向类成员的指针,这将是解决您的问题的一种方法 - 如果您想要的字段在编译时总是已知的。否则,指向成员的指针(或者更灵活的谓词函数对象)可以作为函数参数传递。

标签: c++ code-reuse reusability


【解决方案1】:

是的,您想将 2 个 lambdas (C++11) 或可运行对象 (C++03) 传递到您的“查找”算法中。

使用 C++03,您可以传入 2 个 boost 函数,一个用于“找到”的情况,一个用于“未找到”的情况。

void search( std::string str, 
            boost::function<void()> ifFound, 
           boost::function<void()> ifNotFound )
{
      //search
     if( nodePtr != NULL )
     {
          ifFound();
     }
     else
     {
          ifNotFound();
     }
}

选择要匹配的函数签名,但这就是传递动态函数的方式。

您可以使用std::function 代替boost::function

如果您想让搜索本身变得灵活,即您要尝试匹配对象的哪一部分,也可以使用动态谓词。

void search( Pred pred// , ifFound, ,ifNotFound )
{
     if( pred( nodePtr ) ) // then it is found
}

如您所见,谓词将采用节点指针并返回真/假。因此,将使用不同的谓词来匹配不同的数据成员。

如果你真的喜欢“可重用代码”的概念,我建议你使用标准库。

如果您的列表很长并且您不断进行这些搜索,手动搜索会很慢,您可以使用 boost::multi_index 在各个字段上创建 log-N 时间查找。

【讨论】:

    【解决方案2】:

    类似于std::find_if:

    template<typename N, typename P>
    N* my_find_if(const N* head, P pred)
    {
        N* ptr;
        for (ptr = head; ptr != nullptr && !pred(ptr); ptr = ptr->nextPtr)
            ;
    
        return ptr;
    }
    

    可以这样调用:

    my_find_if(firstPtr,
        [](Node* node){ return node->str == str; });
    

    lambda 更改为您需要的任何表达式。

    当然,我宁愿建议您使用standard containers,而不是创建您自己的列表。然后你可以改用标准的std::find_if

    【讨论】:

    • 作为滚动他自己的列表和使用标准容器之间的中间步骤,可能需要考虑为他的自定义列表编写一个迭代器类型,在这种情况下他也可以使用标准算法。
    【解决方案3】:

    最灵活的方法是提供一个谓词作为模板参数:

    template <typename Pred>
    Node * find_if(Node * node, Pred pred) {
        for (; node && !pred(node); node = node->next);
        return node;
    }
    

    在 C++11 中,您可以使用 lambda 调用它:

    if (Node * node = find_if(first, [&](Node * n){return n->NID == nid;})) {
        // node points to the matching node
    } else {
        // not found
    }
    

    或者,如果你被困在过去的岁月中,一个函数对象:

    struct CompareNID {
        CompareNID(std::string nid) : nid(nid) {}
        bool operator() {Node * n) {return n->NID == nid;}
    
        std::string nid;
    };
    
    Node * node = find_if(first, CompareNID(nid));
    

    或者,由于您的所有字段都是字符串,因此您可以使用成员指针牺牲灵活性以求简洁,给出类似于您的 PHP 示例的内容:

    Node * find(Node * node, std::string Node::*member, std::string const & value) {
        for (; node && node->*member != value; node = node->next);
        return node;
    }
    
    Node * node = find(first, &Node::NID, nid);
    

    【讨论】:

    • 只是错过了 find_if 参数列表中的第二个参数
    【解决方案4】:

    也许你想使用指向成员的指针:

    typedef string Node::*NodeStringPtr;
    NodeStringPtr nodeStrPtr = nullptr;
    std::vector<NodeStringPtr> nodeStrings {&Node::NID, &Node::str, &Node::fName};
    for (auto& ptr : nodeStrings)
    {
      nodePtr = firstPtr;
      for (; nodePtr != NULL && nodePtr->*ptr != str; nodePtr = nodePtr->nextPtr);
      if (nodePtr)
      {
        nodeStrPtr = ptr;
        break;
      }
    }
    
    if(nodePtr != NULL)
        //the nodePtr matches the search key, nodeStrPtr matches the element
    else
        /* ...*/
    

    【讨论】:

      【解决方案5】:

      另一种方法是使用指向成员的指针(仅当所有成员都属于同一类型时才可能):

      struct Item {
          std::string NID,
                      customerNumber,
                      studentNumber,
                      fName,
                      lName;
      };
      
      typedef std::vector<Item>::iterator nodePtr;
      
      typedef std::string Item::* MemberPtr;
      
      struct List {
          std::vector<Item> list;
      
          nodePtr search(const std::string& str, MemberPtr mem_ptr)
          {
              // this is the code you want to reuse, I took the liberty and used
              // standard lib's algorithm
              return std::find_if(list.begin(), list.end(),
                                  [&](const Item& item){ return str == item.*mem_ptr; });
              // will return list.end() if it doesn't find anything
          }
      };
      
      int main()
      {
          List lst;
          lst.search("John", &Item::fName);
          lst.search("Doe", &Item::lName);
          lst.search("42", &Item::customerNumber);
      }
      

      我认为这是最接近 PHP 示例的方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-10-14
        • 1970-01-01
        • 1970-01-01
        • 2017-10-02
        • 1970-01-01
        • 1970-01-01
        • 2020-05-14
        相关资源
        最近更新 更多