1

具体来说,我有一个具有多个字符串对象数据成员(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++ 中是否有重用代码的方法?

4

5 回答 5

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更改为您需要的任何表达式。

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

于 2013-01-29T11:38:52.390 回答
2

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

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);
于 2013-01-29T11:43:17.670 回答
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
}

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

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

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

于 2013-01-29T11:32:26.260 回答
0

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

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
    /* ...*/
于 2013-01-29T11:45:42.857 回答
0

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

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 示例的方法。

于 2013-01-29T11:47:54.857 回答